@whereby.com/core 0.15.1 → 0.16.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
@@ -2,10 +2,10 @@
2
2
 
3
3
  var toolkit = require('@reduxjs/toolkit');
4
4
  var media = require('@whereby.com/media');
5
+ var events$1 = require('events');
5
6
  var Chrome111_js = require('mediasoup-client/lib/handlers/Chrome111.js');
6
7
  var nodeBtoa = require('btoa');
7
8
  var axios = require('axios');
8
- var EventEmitter = require('events');
9
9
 
10
10
  function createAppAsyncThunk(typePrefix, payloadCreator) {
11
11
  return toolkit.createAsyncThunk(typePrefix, payloadCreator);
@@ -45,9 +45,9 @@ const createReactor = (selectors, callback) => {
45
45
  });
46
46
  };
47
47
 
48
- const coreVersion = "0.15.1";
48
+ const coreVersion = "0.16.0";
49
49
 
50
- const initialState$e = {
50
+ const initialState$f = {
51
51
  isNodeSdk: false,
52
52
  isActive: false,
53
53
  roomName: null,
@@ -58,7 +58,7 @@ const initialState$e = {
58
58
  };
59
59
  const appSlice = toolkit.createSlice({
60
60
  name: "app",
61
- initialState: initialState$e,
61
+ initialState: initialState$f,
62
62
  reducers: {
63
63
  doAppStart: (state, action) => {
64
64
  const url = new URL(action.payload.roomUrl);
@@ -103,6 +103,8 @@ const signalEvents = {
103
103
  roomSessionEnded: createSignalEventAction("roomSessionEnded"),
104
104
  screenshareStarted: createSignalEventAction("screenshareStarted"),
105
105
  screenshareStopped: createSignalEventAction("screenshareStopped"),
106
+ spotlightAdded: createSignalEventAction("spotlightAdded"),
107
+ spotlightRemoved: createSignalEventAction("spotlightRemoved"),
106
108
  streamingStopped: createSignalEventAction("streamingStopped"),
107
109
  videoEnabled: createSignalEventAction("videoEnabled"),
108
110
  };
@@ -112,14 +114,16 @@ const ROOM_ACTION_PERMISSIONS_BY_ROLE = {
112
114
  canRequestAudioEnable: ["host"],
113
115
  canKickClient: ["host"],
114
116
  canEndMeeting: ["host"],
117
+ canAskToSpeak: ["host"],
118
+ canSpotlight: ["host"],
115
119
  };
116
- const initialState$d = {
120
+ const initialState$e = {
117
121
  roomKey: null,
118
122
  roleName: "none",
119
123
  };
120
124
  const authorizationSlice = toolkit.createSlice({
121
125
  name: "authorization",
122
- initialState: initialState$d,
126
+ initialState: initialState$e,
123
127
  reducers: {
124
128
  setRoomKey: (state, action) => {
125
129
  return Object.assign(Object.assign({}, state), { roomKey: action.payload });
@@ -143,6 +147,8 @@ const selectIsAuthorizedToLockRoom = toolkit.createSelector(selectAuthorizationR
143
147
  const selectIsAuthorizedToRequestAudioEnable = toolkit.createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestAudioEnable.includes(localParticipantRole));
144
148
  const selectIsAuthorizedToKickClient = toolkit.createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canKickClient.includes(localParticipantRole));
145
149
  const selectIsAuthorizedToEndMeeting = toolkit.createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canEndMeeting.includes(localParticipantRole));
150
+ const selectIsAuthorizedToAskToSpeak = toolkit.createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canAskToSpeak.includes(localParticipantRole));
151
+ const selectIsAuthorizedToSpotlight = toolkit.createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canSpotlight.includes(localParticipantRole));
146
152
 
147
153
  /******************************************************************************
148
154
  Copyright (c) Microsoft Corporation.
@@ -188,13 +194,13 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
188
194
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
189
195
  };
190
196
 
191
- const initialState$c = {
197
+ const initialState$d = {
192
198
  isFetching: false,
193
199
  data: null,
194
200
  };
195
201
  const deviceCredentialsSlice = toolkit.createSlice({
196
202
  name: "deviceCredentials",
197
- initialState: initialState$c,
203
+ initialState: initialState$d,
198
204
  reducers: {},
199
205
  extraReducers: (builder) => {
200
206
  builder.addCase(doGetDeviceCredentials.pending, (state) => {
@@ -254,6 +260,8 @@ function forwardSocketEvents(socket, dispatch) {
254
260
  socket.on("cloud_recording_started", (payload) => dispatch(signalEvents.cloudRecordingStarted(payload)));
255
261
  socket.on("cloud_recording_stopped", () => dispatch(signalEvents.cloudRecordingStopped()));
256
262
  socket.on("streaming_stopped", () => dispatch(signalEvents.streamingStopped()));
263
+ socket.on("spotlight_added", (payload) => dispatch(signalEvents.spotlightAdded(payload)));
264
+ socket.on("spotlight_removed", (payload) => dispatch(signalEvents.spotlightRemoved(payload)));
257
265
  }
258
266
  const SIGNAL_BASE_URL = "wss://signal.appearin.net" ;
259
267
  function createSocket() {
@@ -264,7 +272,7 @@ function createSocket() {
264
272
  };
265
273
  return new media.ServerSocket(socketHost, socketOverrides);
266
274
  }
267
- const initialState$b = {
275
+ const initialState$c = {
268
276
  deviceIdentified: false,
269
277
  isIdentifyingDevice: false,
270
278
  status: "ready",
@@ -272,7 +280,7 @@ const initialState$b = {
272
280
  };
273
281
  const signalConnectionSlice = toolkit.createSlice({
274
282
  name: "signalConnection",
275
- initialState: initialState$b,
283
+ initialState: initialState$c,
276
284
  reducers: {
277
285
  socketConnecting: (state) => {
278
286
  return Object.assign(Object.assign({}, state), { status: "connecting" });
@@ -376,12 +384,12 @@ startAppListening({
376
384
  },
377
385
  });
378
386
 
379
- const initialState$a = {
387
+ const initialState$b = {
380
388
  chatMessages: [],
381
389
  };
382
390
  const chatSlice = toolkit.createSlice({
383
391
  name: "chat",
384
- initialState: initialState$a,
392
+ initialState: initialState$b,
385
393
  reducers: {},
386
394
  extraReducers(builder) {
387
395
  builder.addCase(signalEvents.chatMessage, (state, action) => {
@@ -593,9 +601,11 @@ function parseUnverifiedRoomKeyData(roomKey) {
593
601
  const initialLocalMediaState = {
594
602
  busyDeviceIds: [],
595
603
  cameraEnabled: false,
604
+ currentSpeakerDeviceId: "default",
596
605
  devices: [],
597
606
  isSettingCameraDevice: false,
598
607
  isSettingMicrophoneDevice: false,
608
+ isSettingSpeakerDevice: false,
599
609
  isTogglingCamera: false,
600
610
  lowDataMode: false,
601
611
  microphoneEnabled: false,
@@ -627,6 +637,10 @@ const localMediaSlice = toolkit.createSlice({
627
637
  setCurrentMicrophoneDeviceId(state, action) {
628
638
  return Object.assign(Object.assign({}, state), { currentMicrophoneDeviceId: action.payload.deviceId });
629
639
  },
640
+ setCurrentSpeakerDeviceId(state, action) {
641
+ var _a;
642
+ return Object.assign(Object.assign({}, state), { currentSpeakerDeviceId: (_a = action.payload.deviceId) !== null && _a !== void 0 ? _a : "default" });
643
+ },
630
644
  toggleLowDataModeEnabled(state, action) {
631
645
  var _a;
632
646
  return Object.assign(Object.assign({}, state), { lowDataMode: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.lowDataMode });
@@ -709,7 +723,7 @@ const localMediaSlice = toolkit.createSlice({
709
723
  });
710
724
  },
711
725
  });
712
- const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
726
+ const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
713
727
  const doToggleCamera = createAppAsyncThunk("localMedia/doToggleCamera", (_, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
714
728
  const state = getState();
715
729
  const stream = selectLocalMediaStream(state);
@@ -899,6 +913,7 @@ const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
899
913
  const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
900
914
  const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
901
915
  const selectCurrentMicrophoneDeviceId = (state) => state.localMedia.currentMicrophoneDeviceId;
916
+ const selectCurrentSpeakerDeviceId = (state) => state.localMedia.currentSpeakerDeviceId;
902
917
  const selectIsCameraEnabled = (state) => state.localMedia.cameraEnabled;
903
918
  const selectIsMicrophoneEnabled = (state) => state.localMedia.microphoneEnabled;
904
919
  const selectIsLowDataModeEnabled = (state) => state.localMedia.lowDataMode;
@@ -1035,7 +1050,7 @@ startAppListening({
1035
1050
  },
1036
1051
  });
1037
1052
 
1038
- const initialState$9 = {
1053
+ const initialState$a = {
1039
1054
  displayName: "",
1040
1055
  id: "",
1041
1056
  isAudioEnabled: true,
@@ -1045,34 +1060,14 @@ const initialState$9 = {
1045
1060
  isScreenSharing: false,
1046
1061
  roleName: "none",
1047
1062
  clientClaim: undefined,
1063
+ stickyReaction: undefined,
1048
1064
  };
1049
- const doEnableAudio = createAppAsyncThunk("localParticipant/doEnableAudio", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1050
- const state = getState();
1051
- const socket = selectSignalConnectionRaw(state).socket;
1052
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
1053
- return payload.enabled;
1054
- }));
1055
- const doEnableVideo = createAppAsyncThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1056
- const state = getState();
1057
- const socket = selectSignalConnectionRaw(state).socket;
1058
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
1059
- return payload.enabled;
1060
- }));
1061
- const doSetDisplayName = createAppAsyncThunk("localParticipant/doSetDisplayName", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1062
- const state = getState();
1063
- const socket = selectSignalConnectionRaw(state).socket;
1064
- socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1065
- type: "UserData",
1066
- payload,
1067
- });
1068
- return payload.displayName;
1069
- }));
1070
1065
  const localParticipantSlice = toolkit.createSlice({
1071
1066
  name: "localParticipant",
1072
- initialState: initialState$9,
1067
+ initialState: initialState$a,
1073
1068
  reducers: {
1074
- doSetLocalParticipant: (state, action) => {
1075
- return Object.assign(Object.assign({}, state), action.payload);
1069
+ doSetDisplayName: (state, action) => {
1070
+ return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
1076
1071
  },
1077
1072
  },
1078
1073
  extraReducers: (builder) => {
@@ -1085,8 +1080,8 @@ const localParticipantSlice = toolkit.createSlice({
1085
1080
  builder.addCase(doEnableVideo.fulfilled, (state, action) => {
1086
1081
  return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
1087
1082
  });
1088
- builder.addCase(doSetDisplayName.fulfilled, (state, action) => {
1089
- return Object.assign(Object.assign({}, state), { displayName: action.payload });
1083
+ builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
1084
+ return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
1090
1085
  });
1091
1086
  builder.addCase(signalEvents.roomJoined, (state, action) => {
1092
1087
  var _a, _b;
@@ -1095,11 +1090,64 @@ const localParticipantSlice = toolkit.createSlice({
1095
1090
  });
1096
1091
  },
1097
1092
  });
1098
- const { doSetLocalParticipant } = localParticipantSlice.actions;
1093
+ const { doSetDisplayName } = localParticipantSlice.actions;
1094
+ const doEnableAudio = createAppAsyncThunk("localParticipant/doEnableAudio", (payload, { dispatch, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1095
+ const state = getState();
1096
+ const socket = selectSignalConnectionRaw(state).socket;
1097
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
1098
+ if (payload.enabled) {
1099
+ dispatch(doSetLocalStickyReaction({ enabled: false }));
1100
+ }
1101
+ return payload.enabled;
1102
+ }));
1103
+ const doEnableVideo = createAppAsyncThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1104
+ const state = getState();
1105
+ const socket = selectSignalConnectionRaw(state).socket;
1106
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
1107
+ return payload.enabled;
1108
+ }));
1109
+ const doSetLocalStickyReaction = createAppAsyncThunk("localParticipant/doSetLocalStickyReaction", (payload, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
1110
+ var _a;
1111
+ const state = getState();
1112
+ const currentStickyReaction = selectLocalParticipantStickyReaction(state);
1113
+ const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
1114
+ const enabled = (_a = payload.enabled) !== null && _a !== void 0 ? _a : !stickyReactionCurrentlyEnabled;
1115
+ if (enabled === stickyReactionCurrentlyEnabled) {
1116
+ return rejectWithValue(currentStickyReaction);
1117
+ }
1118
+ const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
1119
+ return stickyReaction;
1120
+ }));
1121
+ const doSendClientMetadata = createAppThunk(() => (_, getState) => {
1122
+ const state = getState();
1123
+ const socket = selectSignalConnectionRaw(state).socket;
1124
+ const payload = {
1125
+ displayName: selectLocalParticipantDisplayName(state),
1126
+ stickyReaction: selectLocalParticipantStickyReaction(state),
1127
+ };
1128
+ socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1129
+ type: "UserData",
1130
+ payload,
1131
+ });
1132
+ });
1099
1133
  const selectLocalParticipantRaw = (state) => state.localParticipant;
1100
1134
  const selectSelfId = (state) => state.localParticipant.id;
1135
+ const selectLocalParticipantDisplayName = (state) => state.localParticipant.displayName;
1101
1136
  const selectLocalParticipantClientClaim = (state) => state.localParticipant.clientClaim;
1102
1137
  const selectLocalParticipantIsScreenSharing = (state) => state.localParticipant.isScreenSharing;
1138
+ const selectLocalParticipantStickyReaction = (state) => state.localParticipant.stickyReaction;
1139
+ const selectLocalParticipantView = toolkit.createSelector(selectLocalParticipantRaw, selectLocalMediaStream, (participant, localStream) => {
1140
+ const clientView = {
1141
+ id: participant.id,
1142
+ clientId: participant.id,
1143
+ displayName: participant.displayName,
1144
+ stream: localStream,
1145
+ isLocalClient: true,
1146
+ isAudioEnabled: participant.isAudioEnabled,
1147
+ isVideoEnabled: participant.isVideoEnabled,
1148
+ };
1149
+ return clientView;
1150
+ });
1103
1151
  startAppListening({
1104
1152
  actionCreator: toggleCameraEnabled,
1105
1153
  effect: ({ payload }, { dispatch, getState }) => {
@@ -1116,15 +1164,18 @@ startAppListening({
1116
1164
  dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
1117
1165
  },
1118
1166
  });
1167
+ createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction], ({ dispatch }) => {
1168
+ dispatch(doSendClientMetadata());
1169
+ });
1119
1170
 
1120
- const initialState$8 = {
1171
+ const initialState$9 = {
1121
1172
  status: "inactive",
1122
1173
  stream: null,
1123
1174
  error: null,
1124
1175
  };
1125
1176
  const localScreenshareSlice = toolkit.createSlice({
1126
1177
  name: "localScreenshare",
1127
- initialState: initialState$8,
1178
+ initialState: initialState$9,
1128
1179
  reducers: {
1129
1180
  stopScreenshare(state, action) {
1130
1181
  return Object.assign(Object.assign({}, state), { status: "inactive", stream: null });
@@ -1193,60 +1244,6 @@ startAppListening({
1193
1244
  },
1194
1245
  });
1195
1246
 
1196
- const initialState$7 = {
1197
- data: null,
1198
- isFetching: false,
1199
- error: null,
1200
- };
1201
- const organizationSlice = toolkit.createSlice({
1202
- initialState: initialState$7,
1203
- name: "organization",
1204
- reducers: {},
1205
- extraReducers: (builder) => {
1206
- builder.addCase(doOrganizationFetch.pending, (state) => {
1207
- return Object.assign(Object.assign({}, state), { isFetching: true });
1208
- });
1209
- builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
1210
- if (!action.payload)
1211
- return Object.assign(Object.assign({}, state), { isFetching: true });
1212
- return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
1213
- });
1214
- builder.addCase(doOrganizationFetch.rejected, (state) => {
1215
- return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
1216
- });
1217
- },
1218
- });
1219
- const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_, { extra, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1220
- try {
1221
- const roomUrl = selectAppRoomUrl(getState());
1222
- const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
1223
- if (!organization) {
1224
- throw new Error("Invalid room url");
1225
- }
1226
- return organization;
1227
- }
1228
- catch (error) {
1229
- console.error(error);
1230
- }
1231
- }));
1232
- const selectOrganizationRaw = (state) => state.organization;
1233
- const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
1234
- const selectShouldFetchOrganization = toolkit.createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
1235
- if (appIsActive &&
1236
- !organization.data &&
1237
- !organization.isFetching &&
1238
- !organization.error &&
1239
- !deviceCredentials.isFetching) {
1240
- return true;
1241
- }
1242
- return false;
1243
- });
1244
- createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
1245
- if (shouldFetchOrganization) {
1246
- dispatch(doOrganizationFetch());
1247
- }
1248
- });
1249
-
1250
1247
  function createRtcEventAction(name) {
1251
1248
  return toolkit.createAction(`rtcConnection/event/${name}`);
1252
1249
  }
@@ -1348,12 +1345,12 @@ function addStream(state, payload) {
1348
1345
  presentationStream: stream,
1349
1346
  });
1350
1347
  }
1351
- const initialState$6 = {
1348
+ const initialState$8 = {
1352
1349
  remoteParticipants: [],
1353
1350
  };
1354
1351
  const remoteParticipantsSlice = toolkit.createSlice({
1355
1352
  name: "remoteParticipants",
1356
- initialState: initialState$6,
1353
+ initialState: initialState$8,
1357
1354
  reducers: {
1358
1355
  streamStatusUpdated: (state, action) => {
1359
1356
  let newState = state;
@@ -1412,9 +1409,15 @@ const remoteParticipantsSlice = toolkit.createSlice({
1412
1409
  });
1413
1410
  });
1414
1411
  builder.addCase(signalEvents.clientMetadataReceived, (state, action) => {
1415
- const { clientId, displayName } = action.payload.payload;
1412
+ const { error, payload } = action.payload;
1413
+ if (error || !payload) {
1414
+ console.warn(error || "Client metadata error received");
1415
+ return state;
1416
+ }
1417
+ const { clientId, displayName, stickyReaction } = payload;
1416
1418
  return updateParticipant(state, clientId, {
1417
1419
  displayName,
1420
+ stickyReaction,
1418
1421
  });
1419
1422
  });
1420
1423
  builder.addCase(signalEvents.screenshareStarted, (state, action) => {
@@ -1430,87 +1433,79 @@ const remoteParticipantsSlice = toolkit.createSlice({
1430
1433
  const { participantStreamAdded, participantStreamIdAdded, streamStatusUpdated } = remoteParticipantsSlice.actions;
1431
1434
  const doRequestAudioEnable = createAppAuthorizedThunk((state) => selectIsAuthorizedToRequestAudioEnable(state), (payload) => (_, getState) => {
1432
1435
  const state = getState();
1436
+ const canEnableRemoteAudio = selectIsAuthorizedToAskToSpeak(state);
1437
+ if (payload.enable && !canEnableRemoteAudio) {
1438
+ console.warn("Not authorized to perform this action");
1439
+ return;
1440
+ }
1433
1441
  const socket = selectSignalConnectionRaw(state).socket;
1434
1442
  socket === null || socket === void 0 ? void 0 : socket.emit("request_audio_enable", payload);
1435
1443
  });
1436
1444
  const selectRemoteParticipantsRaw = (state) => state.remoteParticipants;
1437
1445
  const selectRemoteParticipants = (state) => state.remoteParticipants.remoteParticipants;
1438
- const selectScreenshares = toolkit.createSelector(selectLocalScreenshareStream, selectRemoteParticipants, (localScreenshareStream, remoteParticipants) => {
1439
- const screenshares = [];
1440
- if (localScreenshareStream) {
1441
- screenshares.push({
1442
- id: localScreenshareStream.id || "local-screenshare",
1443
- participantId: "local",
1444
- hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1445
- stream: localScreenshareStream,
1446
- isLocal: true,
1447
- });
1448
- }
1449
- for (const participant of remoteParticipants) {
1450
- if (participant.presentationStream) {
1451
- screenshares.push({
1452
- id: participant.presentationStream.id || `pres-${participant.id}`,
1453
- participantId: participant.id,
1454
- hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1455
- stream: participant.presentationStream,
1456
- isLocal: false,
1457
- });
1458
- }
1459
- }
1460
- return screenshares;
1461
- });
1446
+ const selectNumParticipants = toolkit.createSelector(selectRemoteParticipants, (clients) => clients.filter((c) => !NON_PERSON_ROLES.includes(c.roleName)).length + 1);
1462
1447
 
1463
- const initialState$5 = {
1464
- isLocked: false,
1448
+ const initialState$7 = {
1449
+ data: null,
1450
+ isFetching: false,
1451
+ error: null,
1465
1452
  };
1466
- const roomSlice = toolkit.createSlice({
1467
- name: "room",
1468
- initialState: initialState$5,
1453
+ const organizationSlice = toolkit.createSlice({
1454
+ initialState: initialState$7,
1455
+ name: "organization",
1469
1456
  reducers: {},
1470
1457
  extraReducers: (builder) => {
1471
- builder.addCase(signalEvents.roomJoined, (state, action) => {
1472
- const { error, isLocked } = action.payload;
1473
- if (error) {
1474
- return state;
1475
- }
1476
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1458
+ builder.addCase(doOrganizationFetch.pending, (state) => {
1459
+ return Object.assign(Object.assign({}, state), { isFetching: true });
1477
1460
  });
1478
- builder.addCase(signalEvents.roomLocked, (state, action) => {
1479
- const { isLocked } = action.payload;
1480
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1461
+ builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
1462
+ if (!action.payload)
1463
+ return Object.assign(Object.assign({}, state), { isFetching: true });
1464
+ return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
1465
+ });
1466
+ builder.addCase(doOrganizationFetch.rejected, (state) => {
1467
+ return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
1481
1468
  });
1482
1469
  },
1483
1470
  });
1484
- const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1485
- const state = getState();
1486
- const { socket } = selectSignalConnectionRaw(state);
1487
- socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1488
- });
1489
- const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1490
- const state = getState();
1491
- const { socket } = selectSignalConnectionRaw(state);
1492
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1493
- });
1494
- const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1495
- const state = getState();
1496
- const clientsToKick = selectRemoteParticipants(state).map((c) => c.id);
1497
- if (clientsToKick.length) {
1498
- const { socket } = selectSignalConnectionRaw(state);
1499
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1471
+ const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_, { extra, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1472
+ try {
1473
+ const roomUrl = selectAppRoomUrl(getState());
1474
+ const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
1475
+ if (!organization) {
1476
+ throw new Error("Invalid room url");
1477
+ }
1478
+ return organization;
1500
1479
  }
1501
- if (!payload.stayBehind) {
1502
- dispatch(doAppStop());
1480
+ catch (error) {
1481
+ console.error(error);
1482
+ }
1483
+ }));
1484
+ const selectOrganizationRaw = (state) => state.organization;
1485
+ const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
1486
+ const selectShouldFetchOrganization = toolkit.createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
1487
+ if (appIsActive &&
1488
+ !organization.data &&
1489
+ !organization.isFetching &&
1490
+ !organization.error &&
1491
+ !deviceCredentials.isFetching) {
1492
+ return true;
1493
+ }
1494
+ return false;
1495
+ });
1496
+ createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
1497
+ if (shouldFetchOrganization) {
1498
+ dispatch(doOrganizationFetch());
1503
1499
  }
1504
1500
  });
1505
- const selectRoomIsLocked = (state) => state.room.isLocked;
1506
1501
 
1507
- const initialState$4 = {
1502
+ const initialState$6 = {
1508
1503
  session: null,
1509
1504
  status: "ready",
1510
1505
  error: null,
1511
1506
  };
1512
1507
  const roomConnectionSlice = toolkit.createSlice({
1513
- initialState: initialState$4,
1508
+ initialState: initialState$6,
1514
1509
  name: "roomConnection",
1515
1510
  reducers: {
1516
1511
  connectionStatusChanged: (state, action) => {
@@ -1669,6 +1664,249 @@ startAppListening({
1669
1664
  },
1670
1665
  });
1671
1666
 
1667
+ const emitter = new events$1.EventEmitter();
1668
+ function createNotificationEvent(payload) {
1669
+ const notificationEvent = Object.assign(Object.assign({}, payload), { timestamp: Date.now() });
1670
+ return notificationEvent;
1671
+ }
1672
+ const initialNotificationsState = {
1673
+ emitter,
1674
+ events: [],
1675
+ };
1676
+ const notificationsSlice = toolkit.createSlice({
1677
+ name: "notifications",
1678
+ initialState: initialNotificationsState,
1679
+ reducers: {
1680
+ addNotification: (state, action) => {
1681
+ return Object.assign(Object.assign({}, state), { events: [...state.events, Object.assign({}, action.payload)] });
1682
+ },
1683
+ doClearNotifications: (state) => {
1684
+ return Object.assign(Object.assign({}, state), { events: [] });
1685
+ },
1686
+ },
1687
+ });
1688
+ const { doClearNotifications } = notificationsSlice.actions;
1689
+ const doSetNotification = createAppThunk((payload) => (dispatch, getState) => {
1690
+ dispatch(notificationsSlice.actions.addNotification(payload));
1691
+ const state = getState();
1692
+ const emitter = selectNotificationsEmitter(state);
1693
+ emitter.emit(payload.type, payload);
1694
+ emitter.emit("*", payload);
1695
+ });
1696
+ const selectNotificationsRaw = (state) => state.notifications;
1697
+ const selectNotificationsEvents = (state) => state.notifications.events;
1698
+ const selectNotificationsEmitter = (state) => state.notifications.emitter;
1699
+ startAppListening({
1700
+ actionCreator: signalEvents.chatMessage,
1701
+ effect: ({ payload }, { dispatch, getState }) => {
1702
+ const state = getState();
1703
+ const client = selectRemoteParticipants(state).find(({ id }) => id === payload.senderId);
1704
+ if (!client) {
1705
+ console.warn("Could not find remote client that sent chat message");
1706
+ return;
1707
+ }
1708
+ dispatch(doSetNotification(createNotificationEvent({
1709
+ type: "chatMessageReceived",
1710
+ message: `${client.displayName} says: ${payload.text}`,
1711
+ props: {
1712
+ client,
1713
+ chatMessage: {
1714
+ senderId: payload.senderId,
1715
+ timestamp: payload.timestamp,
1716
+ text: payload.text,
1717
+ },
1718
+ },
1719
+ })));
1720
+ },
1721
+ });
1722
+ startAppListening({
1723
+ actionCreator: signalEvents.audioEnableRequested,
1724
+ effect: ({ payload }, { dispatch, getState }) => {
1725
+ const { enable, requestedByClientId } = payload;
1726
+ const state = getState();
1727
+ const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
1728
+ if (!client) {
1729
+ console.warn("Could not find remote client that requested a local audio change");
1730
+ return;
1731
+ }
1732
+ dispatch(doSetNotification(createNotificationEvent({
1733
+ type: enable ? "requestAudioEnable" : "requestAudioDisable",
1734
+ message: enable
1735
+ ? `${client.displayName} has requested for you to speak`
1736
+ : `${client.displayName} has muted your microphone`,
1737
+ props: {
1738
+ client,
1739
+ enable,
1740
+ },
1741
+ })));
1742
+ },
1743
+ });
1744
+ startAppListening({
1745
+ actionCreator: signalEvents.clientMetadataReceived,
1746
+ effect: (action, { dispatch, getOriginalState, getState }) => {
1747
+ var _a;
1748
+ const { error, payload } = action.payload;
1749
+ if (error || !payload) {
1750
+ return;
1751
+ }
1752
+ const { clientId, stickyReaction } = payload;
1753
+ const state = getState();
1754
+ const canAskToSpeak = selectIsAuthorizedToAskToSpeak(state);
1755
+ if (!canAskToSpeak) {
1756
+ return;
1757
+ }
1758
+ const client = selectRemoteParticipants(state).find(({ id }) => id === clientId);
1759
+ if (!client) {
1760
+ console.warn("Could not find remote client that provided updated metadata");
1761
+ return;
1762
+ }
1763
+ const previousState = getOriginalState();
1764
+ const previousClient = selectRemoteParticipants(previousState).find(({ id }) => id === clientId);
1765
+ if ((!stickyReaction && !(previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction)) ||
1766
+ (stickyReaction === null || stickyReaction === void 0 ? void 0 : stickyReaction.timestamp) === ((_a = previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction) === null || _a === void 0 ? void 0 : _a.timestamp)) {
1767
+ return;
1768
+ }
1769
+ dispatch(doSetNotification(createNotificationEvent({
1770
+ type: stickyReaction ? "remoteHandRaised" : "remoteHandLowered",
1771
+ message: `${client.displayName} ${stickyReaction ? "raised" : "lowered"} their hand`,
1772
+ props: {
1773
+ client,
1774
+ stickyReaction,
1775
+ },
1776
+ })));
1777
+ },
1778
+ });
1779
+ createReactor([selectSignalStatus], ({ dispatch, getState }, signalStatus) => {
1780
+ const state = getState();
1781
+ const roomConnectionStatus = selectRoomConnectionStatus(state);
1782
+ if (["left", "kicked"].includes(roomConnectionStatus)) {
1783
+ return;
1784
+ }
1785
+ if (signalStatus === "disconnected") {
1786
+ dispatch(doSetNotification(createNotificationEvent({
1787
+ type: "signalTrouble",
1788
+ message: `Network connection lost. Trying to reconnect you...`,
1789
+ props: {},
1790
+ })));
1791
+ }
1792
+ else if (signalStatus === "connected") {
1793
+ dispatch(doSetNotification(createNotificationEvent({
1794
+ type: "signalOk",
1795
+ message: `Network connection available`,
1796
+ props: {},
1797
+ })));
1798
+ }
1799
+ });
1800
+
1801
+ function isStreamerClient(client) {
1802
+ return client.roleName === "streamer";
1803
+ }
1804
+ function isRecorderClient(client) {
1805
+ return client.roleName === "recorder";
1806
+ }
1807
+ const initialState$5 = {
1808
+ isLocked: false,
1809
+ };
1810
+ const roomSlice = toolkit.createSlice({
1811
+ name: "room",
1812
+ initialState: initialState$5,
1813
+ reducers: {},
1814
+ extraReducers: (builder) => {
1815
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
1816
+ const { error, isLocked } = action.payload;
1817
+ if (error) {
1818
+ return state;
1819
+ }
1820
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1821
+ });
1822
+ builder.addCase(signalEvents.roomLocked, (state, action) => {
1823
+ const { isLocked } = action.payload;
1824
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1825
+ });
1826
+ },
1827
+ });
1828
+ const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1829
+ const state = getState();
1830
+ const { socket } = selectSignalConnectionRaw(state);
1831
+ socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1832
+ });
1833
+ const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1834
+ const state = getState();
1835
+ const { socket } = selectSignalConnectionRaw(state);
1836
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1837
+ });
1838
+ const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1839
+ const state = getState();
1840
+ const clientsToKick = selectRemoteParticipants(state).map((c) => c.id);
1841
+ if (clientsToKick.length) {
1842
+ const { socket } = selectSignalConnectionRaw(state);
1843
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1844
+ }
1845
+ if (!payload.stayBehind) {
1846
+ dispatch(doAppStop());
1847
+ }
1848
+ });
1849
+ const selectRoomIsLocked = (state) => state.room.isLocked;
1850
+ const selectScreenshares = toolkit.createSelector(selectLocalScreenshareStream, selectRemoteParticipants, (localScreenshareStream, remoteParticipants) => {
1851
+ const screenshares = [];
1852
+ if (localScreenshareStream) {
1853
+ screenshares.push({
1854
+ id: localScreenshareStream.id || "local-screenshare",
1855
+ participantId: "local",
1856
+ hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1857
+ stream: localScreenshareStream,
1858
+ isLocal: true,
1859
+ });
1860
+ }
1861
+ for (const participant of remoteParticipants) {
1862
+ if (participant.presentationStream) {
1863
+ screenshares.push({
1864
+ id: participant.presentationStream.id || `pres-${participant.id}`,
1865
+ participantId: participant.id,
1866
+ hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1867
+ stream: participant.presentationStream,
1868
+ isLocal: false,
1869
+ });
1870
+ }
1871
+ }
1872
+ return screenshares;
1873
+ });
1874
+ const selectRemoteClientViews = toolkit.createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
1875
+ const views = [];
1876
+ if (localScreenshareStream) {
1877
+ const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
1878
+ views.push({
1879
+ clientId: localParticipant.id,
1880
+ displayName: "Your screenshare",
1881
+ id: "local-screenshare",
1882
+ isAudioEnabled: isScreenshareAudioEnabled,
1883
+ isLocalClient: true,
1884
+ isPresentation: true,
1885
+ isVideoEnabled: true,
1886
+ stream: localScreenshareStream,
1887
+ });
1888
+ }
1889
+ for (const c of remoteParticipants) {
1890
+ if (isStreamerClient(c) || isRecorderClient(c)) {
1891
+ continue;
1892
+ }
1893
+ const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
1894
+ const displayName = c.displayName || "Guest";
1895
+ const isPresentationActive = presentationStream && presentationStream.active;
1896
+ const presentationId = "pres-" + c.id;
1897
+ const isStreamActive = c.stream && c.stream.active;
1898
+ const isVideoEnabled = c.isVideoEnabled;
1899
+ views.push(Object.assign(Object.assign(Object.assign({}, clientView), { clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
1900
+ if (isPresentationActive) {
1901
+ 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 })));
1902
+ }
1903
+ }
1904
+ return views;
1905
+ });
1906
+ const selectAllClientViews = toolkit.createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
1907
+ return [localParticipant, ...remoteParticipants];
1908
+ });
1909
+
1672
1910
  const createWebRtcEmitter = (dispatch) => {
1673
1911
  return {
1674
1912
  emit: (eventName, data) => {
@@ -1685,7 +1923,7 @@ const createWebRtcEmitter = (dispatch) => {
1685
1923
  },
1686
1924
  };
1687
1925
  };
1688
- const initialState$3 = {
1926
+ const initialState$4 = {
1689
1927
  dispatcherCreated: false,
1690
1928
  error: null,
1691
1929
  isCreatingDispatcher: false,
@@ -1698,7 +1936,7 @@ const initialState$3 = {
1698
1936
  };
1699
1937
  const rtcConnectionSlice = toolkit.createSlice({
1700
1938
  name: "rtcConnection",
1701
- initialState: initialState$3,
1939
+ initialState: initialState$4,
1702
1940
  reducers: {
1703
1941
  isAcceptingStreams: (state, action) => {
1704
1942
  return Object.assign(Object.assign({}, state), { isAcceptingStreams: action.payload });
@@ -1708,7 +1946,7 @@ const rtcConnectionSlice = toolkit.createSlice({
1708
1946
  return Object.assign(Object.assign({}, state), { reportedStreamResolutions: Object.assign(Object.assign({}, state.reportedStreamResolutions), { [streamId]: { width, height } }) });
1709
1947
  },
1710
1948
  rtcDisconnected: () => {
1711
- return Object.assign({}, initialState$3);
1949
+ return Object.assign({}, initialState$4);
1712
1950
  },
1713
1951
  rtcDispatcherCreated: (state, action) => {
1714
1952
  return Object.assign(Object.assign({}, state), { dispatcherCreated: true, rtcManagerDispatcher: action.payload });
@@ -2006,7 +2244,7 @@ const rtcAnalyticsCustomEvents = {
2006
2244
  getOutput: () => ({}),
2007
2245
  },
2008
2246
  displayName: {
2009
- actions: [doSetDisplayName.fulfilled],
2247
+ actions: [doSetDisplayName],
2010
2248
  rtcEventName: "displayName",
2011
2249
  getValue: (state) => selectAppDisplayName(state),
2012
2250
  getOutput: (value) => ({ displayName: value }),
@@ -2073,11 +2311,11 @@ const makeComparable = (value) => {
2073
2311
  return JSON.stringify(value);
2074
2312
  return value;
2075
2313
  };
2076
- const initialState$2 = {
2314
+ const initialState$3 = {
2077
2315
  reportedValues: {},
2078
2316
  };
2079
2317
  const rtcAnalyticsSlice = toolkit.createSlice({
2080
- initialState: initialState$2,
2318
+ initialState: initialState$3,
2081
2319
  name: "rtcAnalytics",
2082
2320
  reducers: {
2083
2321
  updateReportedValues(state, action) {
@@ -2137,6 +2375,92 @@ createReactor([selectRtcManagerInitialized], ({ dispatch }, selectRtcManagerInit
2137
2375
  }
2138
2376
  });
2139
2377
 
2378
+ function streamIdForClient({ isPresentation, stream }) {
2379
+ var _a, _b;
2380
+ 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";
2381
+ }
2382
+ function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
2383
+ return !!spotlights.find((s) => {
2384
+ const streamId = streamIdForClient({ isPresentation, stream });
2385
+ return s.clientId === clientId && s.streamId === streamId;
2386
+ });
2387
+ }
2388
+ function mergeSpotlight(spotlights, spotlight) {
2389
+ const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
2390
+ if (found) {
2391
+ return spotlights;
2392
+ }
2393
+ return spotlights.concat(spotlight);
2394
+ }
2395
+ function mapSpotlightsToClientViews(spotlights, clientViews) {
2396
+ return spotlights.reduce((acc, s) => {
2397
+ const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
2398
+ if (clientView && !acc.includes(clientView)) {
2399
+ acc.push(clientView);
2400
+ }
2401
+ return acc;
2402
+ }, []);
2403
+ }
2404
+ const initialState$2 = {
2405
+ sorted: [],
2406
+ };
2407
+ const spotlightsSlice = toolkit.createSlice({
2408
+ name: "spotlights",
2409
+ initialState: initialState$2,
2410
+ reducers: {},
2411
+ extraReducers: (builder) => {
2412
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
2413
+ if (!action.payload.room) {
2414
+ return state;
2415
+ }
2416
+ const { spotlights } = action.payload.room;
2417
+ return Object.assign(Object.assign({}, state), { sorted: spotlights });
2418
+ });
2419
+ builder.addCase(signalEvents.spotlightAdded, (state, action) => {
2420
+ const { clientId, streamId } = action.payload;
2421
+ return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
2422
+ });
2423
+ builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
2424
+ const { clientId, streamId } = action.payload;
2425
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
2426
+ });
2427
+ builder.addMatcher(toolkit.isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
2428
+ const { clientId } = action.payload;
2429
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
2430
+ });
2431
+ },
2432
+ });
2433
+ const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2434
+ const state = getState();
2435
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2436
+ if (!clientView) {
2437
+ return;
2438
+ }
2439
+ const { socket } = selectSignalConnectionRaw(state);
2440
+ const streamId = streamIdForClient(clientView);
2441
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2442
+ socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
2443
+ });
2444
+ const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2445
+ const state = getState();
2446
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2447
+ if (!clientView) {
2448
+ return;
2449
+ }
2450
+ const { socket } = selectSignalConnectionRaw(state);
2451
+ const streamId = streamIdForClient(clientView);
2452
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2453
+ socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
2454
+ });
2455
+ const selectSpotlightsRaw = (state) => state.spotlights;
2456
+ const selectSpotlights = (state) => state.spotlights.sorted;
2457
+ const selectIsLocalParticipantSpotlighted = toolkit.createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
2458
+ return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
2459
+ });
2460
+ const selectSpotlightedClientViews = toolkit.createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
2461
+ return mapSpotlightsToClientViews(spotlights, clientViews);
2462
+ });
2463
+
2140
2464
  const initialState$1 = {
2141
2465
  isStreaming: false,
2142
2466
  error: null,
@@ -2221,6 +2545,7 @@ const appReducer = toolkit.combineReducers({
2221
2545
  localMedia: localMediaSlice.reducer,
2222
2546
  localParticipant: localParticipantSlice.reducer,
2223
2547
  localScreenshare: localScreenshareSlice.reducer,
2548
+ notifications: notificationsSlice.reducer,
2224
2549
  organization: organizationSlice.reducer,
2225
2550
  remoteParticipants: remoteParticipantsSlice.reducer,
2226
2551
  room: roomSlice.reducer,
@@ -2228,6 +2553,7 @@ const appReducer = toolkit.combineReducers({
2228
2553
  rtcAnalytics: rtcAnalyticsSlice.reducer,
2229
2554
  rtcConnection: rtcConnectionSlice.reducer,
2230
2555
  signalConnection: signalConnectionSlice.reducer,
2556
+ spotlights: spotlightsSlice.reducer,
2231
2557
  streaming: streamingSlice.reducer,
2232
2558
  waitingParticipants: waitingParticipantsSlice.reducer,
2233
2559
  });
@@ -2613,7 +2939,7 @@ var localStorage$1 = localStorage;
2613
2939
  const events = {
2614
2940
  CREDENTIALS_SAVED: "credentials_saved",
2615
2941
  };
2616
- class CredentialsService extends EventEmitter {
2942
+ class CredentialsService extends events$1.EventEmitter {
2617
2943
  constructor({ deviceService, credentialsStore, }) {
2618
2944
  super();
2619
2945
  this._deviceService = deviceService;
@@ -3235,18 +3561,19 @@ class RoomService {
3235
3561
  }
3236
3562
 
3237
3563
  class RoomParticipant {
3238
- constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled }) {
3564
+ constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction }) {
3239
3565
  this.isLocalParticipant = false;
3240
3566
  this.displayName = displayName;
3241
3567
  this.id = id;
3242
3568
  this.stream = stream;
3243
3569
  this.isAudioEnabled = isAudioEnabled;
3244
3570
  this.isVideoEnabled = isVideoEnabled;
3571
+ this.stickyReaction = stickyReaction;
3245
3572
  }
3246
3573
  }
3247
3574
  class LocalParticipant extends RoomParticipant {
3248
- constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled }) {
3249
- super({ displayName, id, stream, isAudioEnabled, isVideoEnabled });
3575
+ constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction }) {
3576
+ super({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction });
3250
3577
  this.isLocalParticipant = true;
3251
3578
  }
3252
3579
  }
@@ -3304,6 +3631,7 @@ exports.deviceIdentifying = deviceIdentifying;
3304
3631
  exports.doAcceptWaitingParticipant = doAcceptWaitingParticipant;
3305
3632
  exports.doAppStart = doAppStart;
3306
3633
  exports.doAppStop = doAppStop;
3634
+ exports.doClearNotifications = doClearNotifications;
3307
3635
  exports.doConnectRoom = doConnectRoom;
3308
3636
  exports.doConnectRtc = doConnectRtc;
3309
3637
  exports.doDisconnectRtc = doDisconnectRtc;
@@ -3319,18 +3647,22 @@ exports.doKnockRoom = doKnockRoom;
3319
3647
  exports.doLockRoom = doLockRoom;
3320
3648
  exports.doOrganizationFetch = doOrganizationFetch;
3321
3649
  exports.doRejectWaitingParticipant = doRejectWaitingParticipant;
3650
+ exports.doRemoveSpotlight = doRemoveSpotlight;
3322
3651
  exports.doRequestAudioEnable = doRequestAudioEnable;
3323
3652
  exports.doRtcAnalyticsCustomEventsInitialize = doRtcAnalyticsCustomEventsInitialize;
3324
3653
  exports.doRtcManagerCreated = doRtcManagerCreated;
3325
3654
  exports.doRtcManagerInitialize = doRtcManagerInitialize;
3326
3655
  exports.doRtcReportStreamResolution = doRtcReportStreamResolution;
3327
3656
  exports.doSendChatMessage = doSendChatMessage;
3657
+ exports.doSendClientMetadata = doSendClientMetadata;
3328
3658
  exports.doSetDevice = doSetDevice;
3329
3659
  exports.doSetDisplayName = doSetDisplayName;
3330
- exports.doSetLocalParticipant = doSetLocalParticipant;
3660
+ exports.doSetLocalStickyReaction = doSetLocalStickyReaction;
3661
+ exports.doSetNotification = doSetNotification;
3331
3662
  exports.doSignalConnect = doSignalConnect;
3332
3663
  exports.doSignalDisconnect = doSignalDisconnect;
3333
3664
  exports.doSignalIdentifyDevice = doSignalIdentifyDevice;
3665
+ exports.doSpotlightParticipant = doSpotlightParticipant;
3334
3666
  exports.doStartCloudRecording = doStartCloudRecording;
3335
3667
  exports.doStartLocalMedia = doStartLocalMedia;
3336
3668
  exports.doStartScreenshare = doStartScreenshare;
@@ -3344,15 +3676,19 @@ exports.doUpdateDeviceList = doUpdateDeviceList;
3344
3676
  exports.getAudioTrack = getAudioTrack;
3345
3677
  exports.getFakeMediaStream = getFakeMediaStream;
3346
3678
  exports.getVideoTrack = getVideoTrack;
3679
+ exports.hasValue = hasValue;
3347
3680
  exports.initialCloudRecordingState = initialCloudRecordingState;
3348
3681
  exports.initialLocalMediaState = initialLocalMediaState;
3682
+ exports.initialNotificationsState = initialNotificationsState;
3349
3683
  exports.isAcceptingStreams = isAcceptingStreams;
3684
+ exports.isClientSpotlighted = isClientSpotlighted;
3350
3685
  exports.listenerMiddleware = listenerMiddleware;
3351
3686
  exports.localMediaSlice = localMediaSlice;
3352
3687
  exports.localMediaStopped = localMediaStopped;
3353
3688
  exports.localParticipantSlice = localParticipantSlice;
3354
3689
  exports.localScreenshareSlice = localScreenshareSlice;
3355
3690
  exports.localStreamMetadataUpdated = localStreamMetadataUpdated;
3691
+ exports.notificationsSlice = notificationsSlice;
3356
3692
  exports.observeStore = observeStore;
3357
3693
  exports.organizationSlice = organizationSlice;
3358
3694
  exports.parseRoomUrlAndSubdomain = parseRoomUrlAndSubdomain;
@@ -3373,6 +3709,7 @@ exports.rtcDispatcherCreated = rtcDispatcherCreated;
3373
3709
  exports.rtcManagerCreated = rtcManagerCreated;
3374
3710
  exports.rtcManagerDestroyed = rtcManagerDestroyed;
3375
3711
  exports.rtcManagerInitialized = rtcManagerInitialized;
3712
+ exports.selectAllClientViews = selectAllClientViews;
3376
3713
  exports.selectAppDisplayName = selectAppDisplayName;
3377
3714
  exports.selectAppExternalId = selectAppExternalId;
3378
3715
  exports.selectAppInitialConfig = selectAppInitialConfig;
@@ -3394,17 +3731,21 @@ exports.selectCloudRecordingStartedAt = selectCloudRecordingStartedAt;
3394
3731
  exports.selectCloudRecordingStatus = selectCloudRecordingStatus;
3395
3732
  exports.selectCurrentCameraDeviceId = selectCurrentCameraDeviceId;
3396
3733
  exports.selectCurrentMicrophoneDeviceId = selectCurrentMicrophoneDeviceId;
3734
+ exports.selectCurrentSpeakerDeviceId = selectCurrentSpeakerDeviceId;
3397
3735
  exports.selectDeviceCredentialsRaw = selectDeviceCredentialsRaw;
3398
3736
  exports.selectDeviceId = selectDeviceId;
3399
3737
  exports.selectHasFetchedDeviceCredentials = selectHasFetchedDeviceCredentials;
3400
3738
  exports.selectIsAcceptingStreams = selectIsAcceptingStreams;
3739
+ exports.selectIsAuthorizedToAskToSpeak = selectIsAuthorizedToAskToSpeak;
3401
3740
  exports.selectIsAuthorizedToEndMeeting = selectIsAuthorizedToEndMeeting;
3402
3741
  exports.selectIsAuthorizedToKickClient = selectIsAuthorizedToKickClient;
3403
3742
  exports.selectIsAuthorizedToLockRoom = selectIsAuthorizedToLockRoom;
3404
3743
  exports.selectIsAuthorizedToRequestAudioEnable = selectIsAuthorizedToRequestAudioEnable;
3744
+ exports.selectIsAuthorizedToSpotlight = selectIsAuthorizedToSpotlight;
3405
3745
  exports.selectIsCameraEnabled = selectIsCameraEnabled;
3406
3746
  exports.selectIsCloudRecording = selectIsCloudRecording;
3407
3747
  exports.selectIsLocalMediaStarting = selectIsLocalMediaStarting;
3748
+ exports.selectIsLocalParticipantSpotlighted = selectIsLocalParticipantSpotlighted;
3408
3749
  exports.selectIsLowDataModeEnabled = selectIsLowDataModeEnabled;
3409
3750
  exports.selectIsMicrophoneEnabled = selectIsMicrophoneEnabled;
3410
3751
  exports.selectIsSettingCameraDevice = selectIsSettingCameraDevice;
@@ -3422,15 +3763,23 @@ exports.selectLocalMediaStartError = selectLocalMediaStartError;
3422
3763
  exports.selectLocalMediaStatus = selectLocalMediaStatus;
3423
3764
  exports.selectLocalMediaStream = selectLocalMediaStream;
3424
3765
  exports.selectLocalParticipantClientClaim = selectLocalParticipantClientClaim;
3766
+ exports.selectLocalParticipantDisplayName = selectLocalParticipantDisplayName;
3425
3767
  exports.selectLocalParticipantIsScreenSharing = selectLocalParticipantIsScreenSharing;
3426
3768
  exports.selectLocalParticipantRaw = selectLocalParticipantRaw;
3769
+ exports.selectLocalParticipantStickyReaction = selectLocalParticipantStickyReaction;
3770
+ exports.selectLocalParticipantView = selectLocalParticipantView;
3427
3771
  exports.selectLocalScreenshareRaw = selectLocalScreenshareRaw;
3428
3772
  exports.selectLocalScreenshareStatus = selectLocalScreenshareStatus;
3429
3773
  exports.selectLocalScreenshareStream = selectLocalScreenshareStream;
3430
3774
  exports.selectMicrophoneDeviceError = selectMicrophoneDeviceError;
3431
3775
  exports.selectMicrophoneDevices = selectMicrophoneDevices;
3776
+ exports.selectNotificationsEmitter = selectNotificationsEmitter;
3777
+ exports.selectNotificationsEvents = selectNotificationsEvents;
3778
+ exports.selectNotificationsRaw = selectNotificationsRaw;
3779
+ exports.selectNumParticipants = selectNumParticipants;
3432
3780
  exports.selectOrganizationId = selectOrganizationId;
3433
3781
  exports.selectOrganizationRaw = selectOrganizationRaw;
3782
+ exports.selectRemoteClientViews = selectRemoteClientViews;
3434
3783
  exports.selectRemoteParticipants = selectRemoteParticipants;
3435
3784
  exports.selectRemoteParticipantsRaw = selectRemoteParticipantsRaw;
3436
3785
  exports.selectRoomConnectionError = selectRoomConnectionError;
@@ -3462,12 +3811,16 @@ exports.selectSignalConnectionSocket = selectSignalConnectionSocket;
3462
3811
  exports.selectSignalIsIdentifyingDevice = selectSignalIsIdentifyingDevice;
3463
3812
  exports.selectSignalStatus = selectSignalStatus;
3464
3813
  exports.selectSpeakerDevices = selectSpeakerDevices;
3814
+ exports.selectSpotlightedClientViews = selectSpotlightedClientViews;
3815
+ exports.selectSpotlights = selectSpotlights;
3816
+ exports.selectSpotlightsRaw = selectSpotlightsRaw;
3465
3817
  exports.selectStreamingRaw = selectStreamingRaw;
3466
3818
  exports.selectStreamsToAccept = selectStreamsToAccept;
3467
3819
  exports.selectWaitingParticipants = selectWaitingParticipants;
3468
3820
  exports.selectWaitingParticipantsRaw = selectWaitingParticipantsRaw;
3469
3821
  exports.setCurrentCameraDeviceId = setCurrentCameraDeviceId;
3470
3822
  exports.setCurrentMicrophoneDeviceId = setCurrentMicrophoneDeviceId;
3823
+ exports.setCurrentSpeakerDeviceId = setCurrentSpeakerDeviceId;
3471
3824
  exports.setLocalMediaOptions = setLocalMediaOptions;
3472
3825
  exports.setLocalMediaStream = setLocalMediaStream;
3473
3826
  exports.setRoomKey = setRoomKey;
@@ -3477,8 +3830,10 @@ exports.socketConnected = socketConnected;
3477
3830
  exports.socketConnecting = socketConnecting;
3478
3831
  exports.socketDisconnected = socketDisconnected;
3479
3832
  exports.socketReconnecting = socketReconnecting;
3833
+ exports.spotlightsSlice = spotlightsSlice;
3480
3834
  exports.startAppListening = startAppListening;
3481
3835
  exports.stopScreenshare = stopScreenshare;
3836
+ exports.streamIdForClient = streamIdForClient;
3482
3837
  exports.streamStatusUpdated = streamStatusUpdated;
3483
3838
  exports.streamingSlice = streamingSlice;
3484
3839
  exports.toggleCameraEnabled = toggleCameraEnabled;