@whereby.com/core 0.15.2 → 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.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createAsyncThunk, createListenerMiddleware, addListener, createSlice, createAction, createSelector, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
2
2
  import { ServerSocket, getDeviceData, getStream, getUpdatedDevices, RtcManagerDispatcher, assert, fromLocation } from '@whereby.com/media';
3
+ import { EventEmitter } from 'events';
3
4
  import { Chrome111 } from 'mediasoup-client/lib/handlers/Chrome111.js';
4
5
  import nodeBtoa from 'btoa';
5
6
  import axios from 'axios';
6
- import EventEmitter from 'events';
7
7
 
8
8
  function createAppAsyncThunk(typePrefix, payloadCreator) {
9
9
  return createAsyncThunk(typePrefix, payloadCreator);
@@ -43,9 +43,9 @@ const createReactor = (selectors, callback) => {
43
43
  });
44
44
  };
45
45
 
46
- const coreVersion = "0.15.2";
46
+ const coreVersion = "0.16.0";
47
47
 
48
- const initialState$e = {
48
+ const initialState$f = {
49
49
  isNodeSdk: false,
50
50
  isActive: false,
51
51
  roomName: null,
@@ -56,7 +56,7 @@ const initialState$e = {
56
56
  };
57
57
  const appSlice = createSlice({
58
58
  name: "app",
59
- initialState: initialState$e,
59
+ initialState: initialState$f,
60
60
  reducers: {
61
61
  doAppStart: (state, action) => {
62
62
  const url = new URL(action.payload.roomUrl);
@@ -101,6 +101,8 @@ const signalEvents = {
101
101
  roomSessionEnded: createSignalEventAction("roomSessionEnded"),
102
102
  screenshareStarted: createSignalEventAction("screenshareStarted"),
103
103
  screenshareStopped: createSignalEventAction("screenshareStopped"),
104
+ spotlightAdded: createSignalEventAction("spotlightAdded"),
105
+ spotlightRemoved: createSignalEventAction("spotlightRemoved"),
104
106
  streamingStopped: createSignalEventAction("streamingStopped"),
105
107
  videoEnabled: createSignalEventAction("videoEnabled"),
106
108
  };
@@ -110,14 +112,16 @@ const ROOM_ACTION_PERMISSIONS_BY_ROLE = {
110
112
  canRequestAudioEnable: ["host"],
111
113
  canKickClient: ["host"],
112
114
  canEndMeeting: ["host"],
115
+ canAskToSpeak: ["host"],
116
+ canSpotlight: ["host"],
113
117
  };
114
- const initialState$d = {
118
+ const initialState$e = {
115
119
  roomKey: null,
116
120
  roleName: "none",
117
121
  };
118
122
  const authorizationSlice = createSlice({
119
123
  name: "authorization",
120
- initialState: initialState$d,
124
+ initialState: initialState$e,
121
125
  reducers: {
122
126
  setRoomKey: (state, action) => {
123
127
  return Object.assign(Object.assign({}, state), { roomKey: action.payload });
@@ -141,6 +145,8 @@ const selectIsAuthorizedToLockRoom = createSelector(selectAuthorizationRoleName,
141
145
  const selectIsAuthorizedToRequestAudioEnable = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestAudioEnable.includes(localParticipantRole));
142
146
  const selectIsAuthorizedToKickClient = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canKickClient.includes(localParticipantRole));
143
147
  const selectIsAuthorizedToEndMeeting = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canEndMeeting.includes(localParticipantRole));
148
+ const selectIsAuthorizedToAskToSpeak = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canAskToSpeak.includes(localParticipantRole));
149
+ const selectIsAuthorizedToSpotlight = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canSpotlight.includes(localParticipantRole));
144
150
 
145
151
  /******************************************************************************
146
152
  Copyright (c) Microsoft Corporation.
@@ -186,13 +192,13 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
186
192
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
187
193
  };
188
194
 
189
- const initialState$c = {
195
+ const initialState$d = {
190
196
  isFetching: false,
191
197
  data: null,
192
198
  };
193
199
  const deviceCredentialsSlice = createSlice({
194
200
  name: "deviceCredentials",
195
- initialState: initialState$c,
201
+ initialState: initialState$d,
196
202
  reducers: {},
197
203
  extraReducers: (builder) => {
198
204
  builder.addCase(doGetDeviceCredentials.pending, (state) => {
@@ -252,6 +258,8 @@ function forwardSocketEvents(socket, dispatch) {
252
258
  socket.on("cloud_recording_started", (payload) => dispatch(signalEvents.cloudRecordingStarted(payload)));
253
259
  socket.on("cloud_recording_stopped", () => dispatch(signalEvents.cloudRecordingStopped()));
254
260
  socket.on("streaming_stopped", () => dispatch(signalEvents.streamingStopped()));
261
+ socket.on("spotlight_added", (payload) => dispatch(signalEvents.spotlightAdded(payload)));
262
+ socket.on("spotlight_removed", (payload) => dispatch(signalEvents.spotlightRemoved(payload)));
255
263
  }
256
264
  const SIGNAL_BASE_URL = "wss://signal.appearin.net" ;
257
265
  function createSocket() {
@@ -262,7 +270,7 @@ function createSocket() {
262
270
  };
263
271
  return new ServerSocket(socketHost, socketOverrides);
264
272
  }
265
- const initialState$b = {
273
+ const initialState$c = {
266
274
  deviceIdentified: false,
267
275
  isIdentifyingDevice: false,
268
276
  status: "ready",
@@ -270,7 +278,7 @@ const initialState$b = {
270
278
  };
271
279
  const signalConnectionSlice = createSlice({
272
280
  name: "signalConnection",
273
- initialState: initialState$b,
281
+ initialState: initialState$c,
274
282
  reducers: {
275
283
  socketConnecting: (state) => {
276
284
  return Object.assign(Object.assign({}, state), { status: "connecting" });
@@ -374,12 +382,12 @@ startAppListening({
374
382
  },
375
383
  });
376
384
 
377
- const initialState$a = {
385
+ const initialState$b = {
378
386
  chatMessages: [],
379
387
  };
380
388
  const chatSlice = createSlice({
381
389
  name: "chat",
382
- initialState: initialState$a,
390
+ initialState: initialState$b,
383
391
  reducers: {},
384
392
  extraReducers(builder) {
385
393
  builder.addCase(signalEvents.chatMessage, (state, action) => {
@@ -591,9 +599,11 @@ function parseUnverifiedRoomKeyData(roomKey) {
591
599
  const initialLocalMediaState = {
592
600
  busyDeviceIds: [],
593
601
  cameraEnabled: false,
602
+ currentSpeakerDeviceId: "default",
594
603
  devices: [],
595
604
  isSettingCameraDevice: false,
596
605
  isSettingMicrophoneDevice: false,
606
+ isSettingSpeakerDevice: false,
597
607
  isTogglingCamera: false,
598
608
  lowDataMode: false,
599
609
  microphoneEnabled: false,
@@ -625,6 +635,10 @@ const localMediaSlice = createSlice({
625
635
  setCurrentMicrophoneDeviceId(state, action) {
626
636
  return Object.assign(Object.assign({}, state), { currentMicrophoneDeviceId: action.payload.deviceId });
627
637
  },
638
+ setCurrentSpeakerDeviceId(state, action) {
639
+ var _a;
640
+ return Object.assign(Object.assign({}, state), { currentSpeakerDeviceId: (_a = action.payload.deviceId) !== null && _a !== void 0 ? _a : "default" });
641
+ },
628
642
  toggleLowDataModeEnabled(state, action) {
629
643
  var _a;
630
644
  return Object.assign(Object.assign({}, state), { lowDataMode: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.lowDataMode });
@@ -707,7 +721,7 @@ const localMediaSlice = createSlice({
707
721
  });
708
722
  },
709
723
  });
710
- const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
724
+ const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
711
725
  const doToggleCamera = createAppAsyncThunk("localMedia/doToggleCamera", (_, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
712
726
  const state = getState();
713
727
  const stream = selectLocalMediaStream(state);
@@ -897,6 +911,7 @@ const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
897
911
  const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
898
912
  const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
899
913
  const selectCurrentMicrophoneDeviceId = (state) => state.localMedia.currentMicrophoneDeviceId;
914
+ const selectCurrentSpeakerDeviceId = (state) => state.localMedia.currentSpeakerDeviceId;
900
915
  const selectIsCameraEnabled = (state) => state.localMedia.cameraEnabled;
901
916
  const selectIsMicrophoneEnabled = (state) => state.localMedia.microphoneEnabled;
902
917
  const selectIsLowDataModeEnabled = (state) => state.localMedia.lowDataMode;
@@ -1033,7 +1048,7 @@ startAppListening({
1033
1048
  },
1034
1049
  });
1035
1050
 
1036
- const initialState$9 = {
1051
+ const initialState$a = {
1037
1052
  displayName: "",
1038
1053
  id: "",
1039
1054
  isAudioEnabled: true,
@@ -1043,34 +1058,14 @@ const initialState$9 = {
1043
1058
  isScreenSharing: false,
1044
1059
  roleName: "none",
1045
1060
  clientClaim: undefined,
1061
+ stickyReaction: undefined,
1046
1062
  };
1047
- const doEnableAudio = createAppAsyncThunk("localParticipant/doEnableAudio", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1048
- const state = getState();
1049
- const socket = selectSignalConnectionRaw(state).socket;
1050
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
1051
- return payload.enabled;
1052
- }));
1053
- const doEnableVideo = createAppAsyncThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1054
- const state = getState();
1055
- const socket = selectSignalConnectionRaw(state).socket;
1056
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
1057
- return payload.enabled;
1058
- }));
1059
- const doSetDisplayName = createAppAsyncThunk("localParticipant/doSetDisplayName", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1060
- const state = getState();
1061
- const socket = selectSignalConnectionRaw(state).socket;
1062
- socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1063
- type: "UserData",
1064
- payload,
1065
- });
1066
- return payload.displayName;
1067
- }));
1068
1063
  const localParticipantSlice = createSlice({
1069
1064
  name: "localParticipant",
1070
- initialState: initialState$9,
1065
+ initialState: initialState$a,
1071
1066
  reducers: {
1072
- doSetLocalParticipant: (state, action) => {
1073
- return Object.assign(Object.assign({}, state), action.payload);
1067
+ doSetDisplayName: (state, action) => {
1068
+ return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
1074
1069
  },
1075
1070
  },
1076
1071
  extraReducers: (builder) => {
@@ -1083,8 +1078,8 @@ const localParticipantSlice = createSlice({
1083
1078
  builder.addCase(doEnableVideo.fulfilled, (state, action) => {
1084
1079
  return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
1085
1080
  });
1086
- builder.addCase(doSetDisplayName.fulfilled, (state, action) => {
1087
- return Object.assign(Object.assign({}, state), { displayName: action.payload });
1081
+ builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
1082
+ return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
1088
1083
  });
1089
1084
  builder.addCase(signalEvents.roomJoined, (state, action) => {
1090
1085
  var _a, _b;
@@ -1093,11 +1088,64 @@ const localParticipantSlice = createSlice({
1093
1088
  });
1094
1089
  },
1095
1090
  });
1096
- const { doSetLocalParticipant } = localParticipantSlice.actions;
1091
+ const { doSetDisplayName } = localParticipantSlice.actions;
1092
+ const doEnableAudio = createAppAsyncThunk("localParticipant/doEnableAudio", (payload, { dispatch, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1093
+ const state = getState();
1094
+ const socket = selectSignalConnectionRaw(state).socket;
1095
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
1096
+ if (payload.enabled) {
1097
+ dispatch(doSetLocalStickyReaction({ enabled: false }));
1098
+ }
1099
+ return payload.enabled;
1100
+ }));
1101
+ const doEnableVideo = createAppAsyncThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1102
+ const state = getState();
1103
+ const socket = selectSignalConnectionRaw(state).socket;
1104
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
1105
+ return payload.enabled;
1106
+ }));
1107
+ const doSetLocalStickyReaction = createAppAsyncThunk("localParticipant/doSetLocalStickyReaction", (payload, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
1108
+ var _a;
1109
+ const state = getState();
1110
+ const currentStickyReaction = selectLocalParticipantStickyReaction(state);
1111
+ const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
1112
+ const enabled = (_a = payload.enabled) !== null && _a !== void 0 ? _a : !stickyReactionCurrentlyEnabled;
1113
+ if (enabled === stickyReactionCurrentlyEnabled) {
1114
+ return rejectWithValue(currentStickyReaction);
1115
+ }
1116
+ const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
1117
+ return stickyReaction;
1118
+ }));
1119
+ const doSendClientMetadata = createAppThunk(() => (_, getState) => {
1120
+ const state = getState();
1121
+ const socket = selectSignalConnectionRaw(state).socket;
1122
+ const payload = {
1123
+ displayName: selectLocalParticipantDisplayName(state),
1124
+ stickyReaction: selectLocalParticipantStickyReaction(state),
1125
+ };
1126
+ socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1127
+ type: "UserData",
1128
+ payload,
1129
+ });
1130
+ });
1097
1131
  const selectLocalParticipantRaw = (state) => state.localParticipant;
1098
1132
  const selectSelfId = (state) => state.localParticipant.id;
1133
+ const selectLocalParticipantDisplayName = (state) => state.localParticipant.displayName;
1099
1134
  const selectLocalParticipantClientClaim = (state) => state.localParticipant.clientClaim;
1100
1135
  const selectLocalParticipantIsScreenSharing = (state) => state.localParticipant.isScreenSharing;
1136
+ const selectLocalParticipantStickyReaction = (state) => state.localParticipant.stickyReaction;
1137
+ const selectLocalParticipantView = createSelector(selectLocalParticipantRaw, selectLocalMediaStream, (participant, localStream) => {
1138
+ const clientView = {
1139
+ id: participant.id,
1140
+ clientId: participant.id,
1141
+ displayName: participant.displayName,
1142
+ stream: localStream,
1143
+ isLocalClient: true,
1144
+ isAudioEnabled: participant.isAudioEnabled,
1145
+ isVideoEnabled: participant.isVideoEnabled,
1146
+ };
1147
+ return clientView;
1148
+ });
1101
1149
  startAppListening({
1102
1150
  actionCreator: toggleCameraEnabled,
1103
1151
  effect: ({ payload }, { dispatch, getState }) => {
@@ -1114,15 +1162,18 @@ startAppListening({
1114
1162
  dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
1115
1163
  },
1116
1164
  });
1165
+ createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction], ({ dispatch }) => {
1166
+ dispatch(doSendClientMetadata());
1167
+ });
1117
1168
 
1118
- const initialState$8 = {
1169
+ const initialState$9 = {
1119
1170
  status: "inactive",
1120
1171
  stream: null,
1121
1172
  error: null,
1122
1173
  };
1123
1174
  const localScreenshareSlice = createSlice({
1124
1175
  name: "localScreenshare",
1125
- initialState: initialState$8,
1176
+ initialState: initialState$9,
1126
1177
  reducers: {
1127
1178
  stopScreenshare(state, action) {
1128
1179
  return Object.assign(Object.assign({}, state), { status: "inactive", stream: null });
@@ -1191,60 +1242,6 @@ startAppListening({
1191
1242
  },
1192
1243
  });
1193
1244
 
1194
- const initialState$7 = {
1195
- data: null,
1196
- isFetching: false,
1197
- error: null,
1198
- };
1199
- const organizationSlice = createSlice({
1200
- initialState: initialState$7,
1201
- name: "organization",
1202
- reducers: {},
1203
- extraReducers: (builder) => {
1204
- builder.addCase(doOrganizationFetch.pending, (state) => {
1205
- return Object.assign(Object.assign({}, state), { isFetching: true });
1206
- });
1207
- builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
1208
- if (!action.payload)
1209
- return Object.assign(Object.assign({}, state), { isFetching: true });
1210
- return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
1211
- });
1212
- builder.addCase(doOrganizationFetch.rejected, (state) => {
1213
- return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
1214
- });
1215
- },
1216
- });
1217
- const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_, { extra, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1218
- try {
1219
- const roomUrl = selectAppRoomUrl(getState());
1220
- const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
1221
- if (!organization) {
1222
- throw new Error("Invalid room url");
1223
- }
1224
- return organization;
1225
- }
1226
- catch (error) {
1227
- console.error(error);
1228
- }
1229
- }));
1230
- const selectOrganizationRaw = (state) => state.organization;
1231
- const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
1232
- const selectShouldFetchOrganization = createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
1233
- if (appIsActive &&
1234
- !organization.data &&
1235
- !organization.isFetching &&
1236
- !organization.error &&
1237
- !deviceCredentials.isFetching) {
1238
- return true;
1239
- }
1240
- return false;
1241
- });
1242
- createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
1243
- if (shouldFetchOrganization) {
1244
- dispatch(doOrganizationFetch());
1245
- }
1246
- });
1247
-
1248
1245
  function createRtcEventAction(name) {
1249
1246
  return createAction(`rtcConnection/event/${name}`);
1250
1247
  }
@@ -1346,12 +1343,12 @@ function addStream(state, payload) {
1346
1343
  presentationStream: stream,
1347
1344
  });
1348
1345
  }
1349
- const initialState$6 = {
1346
+ const initialState$8 = {
1350
1347
  remoteParticipants: [],
1351
1348
  };
1352
1349
  const remoteParticipantsSlice = createSlice({
1353
1350
  name: "remoteParticipants",
1354
- initialState: initialState$6,
1351
+ initialState: initialState$8,
1355
1352
  reducers: {
1356
1353
  streamStatusUpdated: (state, action) => {
1357
1354
  let newState = state;
@@ -1415,9 +1412,10 @@ const remoteParticipantsSlice = createSlice({
1415
1412
  console.warn(error || "Client metadata error received");
1416
1413
  return state;
1417
1414
  }
1418
- const { clientId, displayName } = payload;
1415
+ const { clientId, displayName, stickyReaction } = payload;
1419
1416
  return updateParticipant(state, clientId, {
1420
1417
  displayName,
1418
+ stickyReaction,
1421
1419
  });
1422
1420
  });
1423
1421
  builder.addCase(signalEvents.screenshareStarted, (state, action) => {
@@ -1433,87 +1431,79 @@ const remoteParticipantsSlice = createSlice({
1433
1431
  const { participantStreamAdded, participantStreamIdAdded, streamStatusUpdated } = remoteParticipantsSlice.actions;
1434
1432
  const doRequestAudioEnable = createAppAuthorizedThunk((state) => selectIsAuthorizedToRequestAudioEnable(state), (payload) => (_, getState) => {
1435
1433
  const state = getState();
1434
+ const canEnableRemoteAudio = selectIsAuthorizedToAskToSpeak(state);
1435
+ if (payload.enable && !canEnableRemoteAudio) {
1436
+ console.warn("Not authorized to perform this action");
1437
+ return;
1438
+ }
1436
1439
  const socket = selectSignalConnectionRaw(state).socket;
1437
1440
  socket === null || socket === void 0 ? void 0 : socket.emit("request_audio_enable", payload);
1438
1441
  });
1439
1442
  const selectRemoteParticipantsRaw = (state) => state.remoteParticipants;
1440
1443
  const selectRemoteParticipants = (state) => state.remoteParticipants.remoteParticipants;
1441
- const selectScreenshares = createSelector(selectLocalScreenshareStream, selectRemoteParticipants, (localScreenshareStream, remoteParticipants) => {
1442
- const screenshares = [];
1443
- if (localScreenshareStream) {
1444
- screenshares.push({
1445
- id: localScreenshareStream.id || "local-screenshare",
1446
- participantId: "local",
1447
- hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1448
- stream: localScreenshareStream,
1449
- isLocal: true,
1450
- });
1451
- }
1452
- for (const participant of remoteParticipants) {
1453
- if (participant.presentationStream) {
1454
- screenshares.push({
1455
- id: participant.presentationStream.id || `pres-${participant.id}`,
1456
- participantId: participant.id,
1457
- hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1458
- stream: participant.presentationStream,
1459
- isLocal: false,
1460
- });
1461
- }
1462
- }
1463
- return screenshares;
1464
- });
1444
+ const selectNumParticipants = createSelector(selectRemoteParticipants, (clients) => clients.filter((c) => !NON_PERSON_ROLES.includes(c.roleName)).length + 1);
1465
1445
 
1466
- const initialState$5 = {
1467
- isLocked: false,
1446
+ const initialState$7 = {
1447
+ data: null,
1448
+ isFetching: false,
1449
+ error: null,
1468
1450
  };
1469
- const roomSlice = createSlice({
1470
- name: "room",
1471
- initialState: initialState$5,
1451
+ const organizationSlice = createSlice({
1452
+ initialState: initialState$7,
1453
+ name: "organization",
1472
1454
  reducers: {},
1473
1455
  extraReducers: (builder) => {
1474
- builder.addCase(signalEvents.roomJoined, (state, action) => {
1475
- const { error, isLocked } = action.payload;
1476
- if (error) {
1477
- return state;
1478
- }
1479
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1456
+ builder.addCase(doOrganizationFetch.pending, (state) => {
1457
+ return Object.assign(Object.assign({}, state), { isFetching: true });
1480
1458
  });
1481
- builder.addCase(signalEvents.roomLocked, (state, action) => {
1482
- const { isLocked } = action.payload;
1483
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1459
+ builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
1460
+ if (!action.payload)
1461
+ return Object.assign(Object.assign({}, state), { isFetching: true });
1462
+ return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
1463
+ });
1464
+ builder.addCase(doOrganizationFetch.rejected, (state) => {
1465
+ return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
1484
1466
  });
1485
1467
  },
1486
1468
  });
1487
- const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1488
- const state = getState();
1489
- const { socket } = selectSignalConnectionRaw(state);
1490
- socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1491
- });
1492
- const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1493
- const state = getState();
1494
- const { socket } = selectSignalConnectionRaw(state);
1495
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1496
- });
1497
- const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1498
- const state = getState();
1499
- const clientsToKick = selectRemoteParticipants(state).map((c) => c.id);
1500
- if (clientsToKick.length) {
1501
- const { socket } = selectSignalConnectionRaw(state);
1502
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1469
+ const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_, { extra, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1470
+ try {
1471
+ const roomUrl = selectAppRoomUrl(getState());
1472
+ const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
1473
+ if (!organization) {
1474
+ throw new Error("Invalid room url");
1475
+ }
1476
+ return organization;
1503
1477
  }
1504
- if (!payload.stayBehind) {
1505
- dispatch(doAppStop());
1478
+ catch (error) {
1479
+ console.error(error);
1480
+ }
1481
+ }));
1482
+ const selectOrganizationRaw = (state) => state.organization;
1483
+ const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
1484
+ const selectShouldFetchOrganization = createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
1485
+ if (appIsActive &&
1486
+ !organization.data &&
1487
+ !organization.isFetching &&
1488
+ !organization.error &&
1489
+ !deviceCredentials.isFetching) {
1490
+ return true;
1491
+ }
1492
+ return false;
1493
+ });
1494
+ createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
1495
+ if (shouldFetchOrganization) {
1496
+ dispatch(doOrganizationFetch());
1506
1497
  }
1507
1498
  });
1508
- const selectRoomIsLocked = (state) => state.room.isLocked;
1509
1499
 
1510
- const initialState$4 = {
1500
+ const initialState$6 = {
1511
1501
  session: null,
1512
1502
  status: "ready",
1513
1503
  error: null,
1514
1504
  };
1515
1505
  const roomConnectionSlice = createSlice({
1516
- initialState: initialState$4,
1506
+ initialState: initialState$6,
1517
1507
  name: "roomConnection",
1518
1508
  reducers: {
1519
1509
  connectionStatusChanged: (state, action) => {
@@ -1672,6 +1662,249 @@ startAppListening({
1672
1662
  },
1673
1663
  });
1674
1664
 
1665
+ const emitter = new EventEmitter();
1666
+ function createNotificationEvent(payload) {
1667
+ const notificationEvent = Object.assign(Object.assign({}, payload), { timestamp: Date.now() });
1668
+ return notificationEvent;
1669
+ }
1670
+ const initialNotificationsState = {
1671
+ emitter,
1672
+ events: [],
1673
+ };
1674
+ const notificationsSlice = createSlice({
1675
+ name: "notifications",
1676
+ initialState: initialNotificationsState,
1677
+ reducers: {
1678
+ addNotification: (state, action) => {
1679
+ return Object.assign(Object.assign({}, state), { events: [...state.events, Object.assign({}, action.payload)] });
1680
+ },
1681
+ doClearNotifications: (state) => {
1682
+ return Object.assign(Object.assign({}, state), { events: [] });
1683
+ },
1684
+ },
1685
+ });
1686
+ const { doClearNotifications } = notificationsSlice.actions;
1687
+ const doSetNotification = createAppThunk((payload) => (dispatch, getState) => {
1688
+ dispatch(notificationsSlice.actions.addNotification(payload));
1689
+ const state = getState();
1690
+ const emitter = selectNotificationsEmitter(state);
1691
+ emitter.emit(payload.type, payload);
1692
+ emitter.emit("*", payload);
1693
+ });
1694
+ const selectNotificationsRaw = (state) => state.notifications;
1695
+ const selectNotificationsEvents = (state) => state.notifications.events;
1696
+ const selectNotificationsEmitter = (state) => state.notifications.emitter;
1697
+ startAppListening({
1698
+ actionCreator: signalEvents.chatMessage,
1699
+ effect: ({ payload }, { dispatch, getState }) => {
1700
+ const state = getState();
1701
+ const client = selectRemoteParticipants(state).find(({ id }) => id === payload.senderId);
1702
+ if (!client) {
1703
+ console.warn("Could not find remote client that sent chat message");
1704
+ return;
1705
+ }
1706
+ dispatch(doSetNotification(createNotificationEvent({
1707
+ type: "chatMessageReceived",
1708
+ message: `${client.displayName} says: ${payload.text}`,
1709
+ props: {
1710
+ client,
1711
+ chatMessage: {
1712
+ senderId: payload.senderId,
1713
+ timestamp: payload.timestamp,
1714
+ text: payload.text,
1715
+ },
1716
+ },
1717
+ })));
1718
+ },
1719
+ });
1720
+ startAppListening({
1721
+ actionCreator: signalEvents.audioEnableRequested,
1722
+ effect: ({ payload }, { dispatch, getState }) => {
1723
+ const { enable, requestedByClientId } = payload;
1724
+ const state = getState();
1725
+ const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
1726
+ if (!client) {
1727
+ console.warn("Could not find remote client that requested a local audio change");
1728
+ return;
1729
+ }
1730
+ dispatch(doSetNotification(createNotificationEvent({
1731
+ type: enable ? "requestAudioEnable" : "requestAudioDisable",
1732
+ message: enable
1733
+ ? `${client.displayName} has requested for you to speak`
1734
+ : `${client.displayName} has muted your microphone`,
1735
+ props: {
1736
+ client,
1737
+ enable,
1738
+ },
1739
+ })));
1740
+ },
1741
+ });
1742
+ startAppListening({
1743
+ actionCreator: signalEvents.clientMetadataReceived,
1744
+ effect: (action, { dispatch, getOriginalState, getState }) => {
1745
+ var _a;
1746
+ const { error, payload } = action.payload;
1747
+ if (error || !payload) {
1748
+ return;
1749
+ }
1750
+ const { clientId, stickyReaction } = payload;
1751
+ const state = getState();
1752
+ const canAskToSpeak = selectIsAuthorizedToAskToSpeak(state);
1753
+ if (!canAskToSpeak) {
1754
+ return;
1755
+ }
1756
+ const client = selectRemoteParticipants(state).find(({ id }) => id === clientId);
1757
+ if (!client) {
1758
+ console.warn("Could not find remote client that provided updated metadata");
1759
+ return;
1760
+ }
1761
+ const previousState = getOriginalState();
1762
+ const previousClient = selectRemoteParticipants(previousState).find(({ id }) => id === clientId);
1763
+ if ((!stickyReaction && !(previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction)) ||
1764
+ (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)) {
1765
+ return;
1766
+ }
1767
+ dispatch(doSetNotification(createNotificationEvent({
1768
+ type: stickyReaction ? "remoteHandRaised" : "remoteHandLowered",
1769
+ message: `${client.displayName} ${stickyReaction ? "raised" : "lowered"} their hand`,
1770
+ props: {
1771
+ client,
1772
+ stickyReaction,
1773
+ },
1774
+ })));
1775
+ },
1776
+ });
1777
+ createReactor([selectSignalStatus], ({ dispatch, getState }, signalStatus) => {
1778
+ const state = getState();
1779
+ const roomConnectionStatus = selectRoomConnectionStatus(state);
1780
+ if (["left", "kicked"].includes(roomConnectionStatus)) {
1781
+ return;
1782
+ }
1783
+ if (signalStatus === "disconnected") {
1784
+ dispatch(doSetNotification(createNotificationEvent({
1785
+ type: "signalTrouble",
1786
+ message: `Network connection lost. Trying to reconnect you...`,
1787
+ props: {},
1788
+ })));
1789
+ }
1790
+ else if (signalStatus === "connected") {
1791
+ dispatch(doSetNotification(createNotificationEvent({
1792
+ type: "signalOk",
1793
+ message: `Network connection available`,
1794
+ props: {},
1795
+ })));
1796
+ }
1797
+ });
1798
+
1799
+ function isStreamerClient(client) {
1800
+ return client.roleName === "streamer";
1801
+ }
1802
+ function isRecorderClient(client) {
1803
+ return client.roleName === "recorder";
1804
+ }
1805
+ const initialState$5 = {
1806
+ isLocked: false,
1807
+ };
1808
+ const roomSlice = createSlice({
1809
+ name: "room",
1810
+ initialState: initialState$5,
1811
+ reducers: {},
1812
+ extraReducers: (builder) => {
1813
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
1814
+ const { error, isLocked } = action.payload;
1815
+ if (error) {
1816
+ return state;
1817
+ }
1818
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1819
+ });
1820
+ builder.addCase(signalEvents.roomLocked, (state, action) => {
1821
+ const { isLocked } = action.payload;
1822
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1823
+ });
1824
+ },
1825
+ });
1826
+ const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1827
+ const state = getState();
1828
+ const { socket } = selectSignalConnectionRaw(state);
1829
+ socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1830
+ });
1831
+ const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1832
+ const state = getState();
1833
+ const { socket } = selectSignalConnectionRaw(state);
1834
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1835
+ });
1836
+ const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1837
+ const state = getState();
1838
+ const clientsToKick = selectRemoteParticipants(state).map((c) => c.id);
1839
+ if (clientsToKick.length) {
1840
+ const { socket } = selectSignalConnectionRaw(state);
1841
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1842
+ }
1843
+ if (!payload.stayBehind) {
1844
+ dispatch(doAppStop());
1845
+ }
1846
+ });
1847
+ const selectRoomIsLocked = (state) => state.room.isLocked;
1848
+ const selectScreenshares = createSelector(selectLocalScreenshareStream, selectRemoteParticipants, (localScreenshareStream, remoteParticipants) => {
1849
+ const screenshares = [];
1850
+ if (localScreenshareStream) {
1851
+ screenshares.push({
1852
+ id: localScreenshareStream.id || "local-screenshare",
1853
+ participantId: "local",
1854
+ hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1855
+ stream: localScreenshareStream,
1856
+ isLocal: true,
1857
+ });
1858
+ }
1859
+ for (const participant of remoteParticipants) {
1860
+ if (participant.presentationStream) {
1861
+ screenshares.push({
1862
+ id: participant.presentationStream.id || `pres-${participant.id}`,
1863
+ participantId: participant.id,
1864
+ hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1865
+ stream: participant.presentationStream,
1866
+ isLocal: false,
1867
+ });
1868
+ }
1869
+ }
1870
+ return screenshares;
1871
+ });
1872
+ const selectRemoteClientViews = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
1873
+ const views = [];
1874
+ if (localScreenshareStream) {
1875
+ const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
1876
+ views.push({
1877
+ clientId: localParticipant.id,
1878
+ displayName: "Your screenshare",
1879
+ id: "local-screenshare",
1880
+ isAudioEnabled: isScreenshareAudioEnabled,
1881
+ isLocalClient: true,
1882
+ isPresentation: true,
1883
+ isVideoEnabled: true,
1884
+ stream: localScreenshareStream,
1885
+ });
1886
+ }
1887
+ for (const c of remoteParticipants) {
1888
+ if (isStreamerClient(c) || isRecorderClient(c)) {
1889
+ continue;
1890
+ }
1891
+ const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
1892
+ const displayName = c.displayName || "Guest";
1893
+ const isPresentationActive = presentationStream && presentationStream.active;
1894
+ const presentationId = "pres-" + c.id;
1895
+ const isStreamActive = c.stream && c.stream.active;
1896
+ const isVideoEnabled = c.isVideoEnabled;
1897
+ views.push(Object.assign(Object.assign(Object.assign({}, clientView), { clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
1898
+ if (isPresentationActive) {
1899
+ 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 })));
1900
+ }
1901
+ }
1902
+ return views;
1903
+ });
1904
+ const selectAllClientViews = createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
1905
+ return [localParticipant, ...remoteParticipants];
1906
+ });
1907
+
1675
1908
  const createWebRtcEmitter = (dispatch) => {
1676
1909
  return {
1677
1910
  emit: (eventName, data) => {
@@ -1688,7 +1921,7 @@ const createWebRtcEmitter = (dispatch) => {
1688
1921
  },
1689
1922
  };
1690
1923
  };
1691
- const initialState$3 = {
1924
+ const initialState$4 = {
1692
1925
  dispatcherCreated: false,
1693
1926
  error: null,
1694
1927
  isCreatingDispatcher: false,
@@ -1701,7 +1934,7 @@ const initialState$3 = {
1701
1934
  };
1702
1935
  const rtcConnectionSlice = createSlice({
1703
1936
  name: "rtcConnection",
1704
- initialState: initialState$3,
1937
+ initialState: initialState$4,
1705
1938
  reducers: {
1706
1939
  isAcceptingStreams: (state, action) => {
1707
1940
  return Object.assign(Object.assign({}, state), { isAcceptingStreams: action.payload });
@@ -1711,7 +1944,7 @@ const rtcConnectionSlice = createSlice({
1711
1944
  return Object.assign(Object.assign({}, state), { reportedStreamResolutions: Object.assign(Object.assign({}, state.reportedStreamResolutions), { [streamId]: { width, height } }) });
1712
1945
  },
1713
1946
  rtcDisconnected: () => {
1714
- return Object.assign({}, initialState$3);
1947
+ return Object.assign({}, initialState$4);
1715
1948
  },
1716
1949
  rtcDispatcherCreated: (state, action) => {
1717
1950
  return Object.assign(Object.assign({}, state), { dispatcherCreated: true, rtcManagerDispatcher: action.payload });
@@ -2009,7 +2242,7 @@ const rtcAnalyticsCustomEvents = {
2009
2242
  getOutput: () => ({}),
2010
2243
  },
2011
2244
  displayName: {
2012
- actions: [doSetDisplayName.fulfilled],
2245
+ actions: [doSetDisplayName],
2013
2246
  rtcEventName: "displayName",
2014
2247
  getValue: (state) => selectAppDisplayName(state),
2015
2248
  getOutput: (value) => ({ displayName: value }),
@@ -2076,11 +2309,11 @@ const makeComparable = (value) => {
2076
2309
  return JSON.stringify(value);
2077
2310
  return value;
2078
2311
  };
2079
- const initialState$2 = {
2312
+ const initialState$3 = {
2080
2313
  reportedValues: {},
2081
2314
  };
2082
2315
  const rtcAnalyticsSlice = createSlice({
2083
- initialState: initialState$2,
2316
+ initialState: initialState$3,
2084
2317
  name: "rtcAnalytics",
2085
2318
  reducers: {
2086
2319
  updateReportedValues(state, action) {
@@ -2140,6 +2373,92 @@ createReactor([selectRtcManagerInitialized], ({ dispatch }, selectRtcManagerInit
2140
2373
  }
2141
2374
  });
2142
2375
 
2376
+ function streamIdForClient({ isPresentation, stream }) {
2377
+ var _a, _b;
2378
+ 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";
2379
+ }
2380
+ function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
2381
+ return !!spotlights.find((s) => {
2382
+ const streamId = streamIdForClient({ isPresentation, stream });
2383
+ return s.clientId === clientId && s.streamId === streamId;
2384
+ });
2385
+ }
2386
+ function mergeSpotlight(spotlights, spotlight) {
2387
+ const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
2388
+ if (found) {
2389
+ return spotlights;
2390
+ }
2391
+ return spotlights.concat(spotlight);
2392
+ }
2393
+ function mapSpotlightsToClientViews(spotlights, clientViews) {
2394
+ return spotlights.reduce((acc, s) => {
2395
+ const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
2396
+ if (clientView && !acc.includes(clientView)) {
2397
+ acc.push(clientView);
2398
+ }
2399
+ return acc;
2400
+ }, []);
2401
+ }
2402
+ const initialState$2 = {
2403
+ sorted: [],
2404
+ };
2405
+ const spotlightsSlice = createSlice({
2406
+ name: "spotlights",
2407
+ initialState: initialState$2,
2408
+ reducers: {},
2409
+ extraReducers: (builder) => {
2410
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
2411
+ if (!action.payload.room) {
2412
+ return state;
2413
+ }
2414
+ const { spotlights } = action.payload.room;
2415
+ return Object.assign(Object.assign({}, state), { sorted: spotlights });
2416
+ });
2417
+ builder.addCase(signalEvents.spotlightAdded, (state, action) => {
2418
+ const { clientId, streamId } = action.payload;
2419
+ return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
2420
+ });
2421
+ builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
2422
+ const { clientId, streamId } = action.payload;
2423
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
2424
+ });
2425
+ builder.addMatcher(isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
2426
+ const { clientId } = action.payload;
2427
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
2428
+ });
2429
+ },
2430
+ });
2431
+ const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2432
+ const state = getState();
2433
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2434
+ if (!clientView) {
2435
+ return;
2436
+ }
2437
+ const { socket } = selectSignalConnectionRaw(state);
2438
+ const streamId = streamIdForClient(clientView);
2439
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2440
+ socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
2441
+ });
2442
+ const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2443
+ const state = getState();
2444
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2445
+ if (!clientView) {
2446
+ return;
2447
+ }
2448
+ const { socket } = selectSignalConnectionRaw(state);
2449
+ const streamId = streamIdForClient(clientView);
2450
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2451
+ socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
2452
+ });
2453
+ const selectSpotlightsRaw = (state) => state.spotlights;
2454
+ const selectSpotlights = (state) => state.spotlights.sorted;
2455
+ const selectIsLocalParticipantSpotlighted = createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
2456
+ return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
2457
+ });
2458
+ const selectSpotlightedClientViews = createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
2459
+ return mapSpotlightsToClientViews(spotlights, clientViews);
2460
+ });
2461
+
2143
2462
  const initialState$1 = {
2144
2463
  isStreaming: false,
2145
2464
  error: null,
@@ -2224,6 +2543,7 @@ const appReducer = combineReducers({
2224
2543
  localMedia: localMediaSlice.reducer,
2225
2544
  localParticipant: localParticipantSlice.reducer,
2226
2545
  localScreenshare: localScreenshareSlice.reducer,
2546
+ notifications: notificationsSlice.reducer,
2227
2547
  organization: organizationSlice.reducer,
2228
2548
  remoteParticipants: remoteParticipantsSlice.reducer,
2229
2549
  room: roomSlice.reducer,
@@ -2231,6 +2551,7 @@ const appReducer = combineReducers({
2231
2551
  rtcAnalytics: rtcAnalyticsSlice.reducer,
2232
2552
  rtcConnection: rtcConnectionSlice.reducer,
2233
2553
  signalConnection: signalConnectionSlice.reducer,
2554
+ spotlights: spotlightsSlice.reducer,
2234
2555
  streaming: streamingSlice.reducer,
2235
2556
  waitingParticipants: waitingParticipantsSlice.reducer,
2236
2557
  });
@@ -3238,18 +3559,19 @@ class RoomService {
3238
3559
  }
3239
3560
 
3240
3561
  class RoomParticipant {
3241
- constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled }) {
3562
+ constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction }) {
3242
3563
  this.isLocalParticipant = false;
3243
3564
  this.displayName = displayName;
3244
3565
  this.id = id;
3245
3566
  this.stream = stream;
3246
3567
  this.isAudioEnabled = isAudioEnabled;
3247
3568
  this.isVideoEnabled = isVideoEnabled;
3569
+ this.stickyReaction = stickyReaction;
3248
3570
  }
3249
3571
  }
3250
3572
  class LocalParticipant extends RoomParticipant {
3251
- constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled }) {
3252
- super({ displayName, id, stream, isAudioEnabled, isVideoEnabled });
3573
+ constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction }) {
3574
+ super({ displayName, id, stream, isAudioEnabled, isVideoEnabled, stickyReaction });
3253
3575
  this.isLocalParticipant = true;
3254
3576
  }
3255
3577
  }
@@ -3279,4 +3601,4 @@ function createServices() {
3279
3601
  };
3280
3602
  }
3281
3603
 
3282
- export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, appSlice, authorizationSlice, chatSlice, cloudRecordingSlice, createAppAsyncThunk, createAppAuthorizedThunk, createAppThunk, createReactor, createServices, createStore, createWebRtcEmitter, debounce, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppStart, doAppStop, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doEndMeeting, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKickParticipant, doKnockRoom, doLockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRequestAudioEnable, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSetDevice, doSetDisplayName, doSetLocalParticipant, doSignalConnect, doSignalDisconnect, doSignalIdentifyDevice, doStartCloudRecording, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doToggleLowDataMode, doUpdateDeviceList, getAudioTrack, getFakeMediaStream, getVideoTrack, initialCloudRecordingState, initialLocalMediaState, isAcceptingStreams, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, observeStore, organizationSlice, parseRoomUrlAndSubdomain, parseUnverifiedRoomKeyData, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, resolutionReported, roomConnectionSlice, roomSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, selectAppDisplayName, selectAppExternalId, selectAppInitialConfig, selectAppIsActive, selectAppIsNodeSdk, selectAppRaw, selectAppRoomName, selectAppRoomUrl, selectAppUserAgent, selectAuthorizationRoleName, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsAuthorizedToEndMeeting, selectIsAuthorizedToKickClient, selectIsAuthorizedToLockRoom, selectIsAuthorizedToRequestAudioEnable, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsLowDataModeEnabled, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantClientClaim, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectOrganizationId, selectOrganizationRaw, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionError, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRoomIsLocked, selectRoomKey, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, signalEvents, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, startAppListening, stopScreenshare, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleLowDataModeEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };
3604
+ export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, appSlice, authorizationSlice, chatSlice, cloudRecordingSlice, createAppAsyncThunk, createAppAuthorizedThunk, createAppThunk, createReactor, createServices, createStore, createWebRtcEmitter, debounce, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppStart, doAppStop, doClearNotifications, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doEndMeeting, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKickParticipant, doKnockRoom, doLockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRemoveSpotlight, doRequestAudioEnable, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSendClientMetadata, doSetDevice, doSetDisplayName, doSetLocalStickyReaction, doSetNotification, doSignalConnect, doSignalDisconnect, doSignalIdentifyDevice, doSpotlightParticipant, doStartCloudRecording, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doToggleLowDataMode, doUpdateDeviceList, getAudioTrack, getFakeMediaStream, getVideoTrack, hasValue, initialCloudRecordingState, initialLocalMediaState, initialNotificationsState, isAcceptingStreams, isClientSpotlighted, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, notificationsSlice, observeStore, organizationSlice, parseRoomUrlAndSubdomain, parseUnverifiedRoomKeyData, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, resolutionReported, roomConnectionSlice, roomSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, selectAllClientViews, selectAppDisplayName, selectAppExternalId, selectAppInitialConfig, selectAppIsActive, selectAppIsNodeSdk, selectAppRaw, selectAppRoomName, selectAppRoomUrl, selectAppUserAgent, selectAuthorizationRoleName, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectCurrentSpeakerDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsAuthorizedToAskToSpeak, selectIsAuthorizedToEndMeeting, selectIsAuthorizedToKickClient, selectIsAuthorizedToLockRoom, selectIsAuthorizedToRequestAudioEnable, selectIsAuthorizedToSpotlight, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsLocalParticipantSpotlighted, selectIsLowDataModeEnabled, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantClientClaim, selectLocalParticipantDisplayName, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantStickyReaction, selectLocalParticipantView, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectNotificationsEmitter, selectNotificationsEvents, selectNotificationsRaw, selectNumParticipants, selectOrganizationId, selectOrganizationRaw, selectRemoteClientViews, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionError, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRoomIsLocked, selectRoomKey, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectSpotlightedClientViews, selectSpotlights, selectSpotlightsRaw, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, signalEvents, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, spotlightsSlice, startAppListening, stopScreenshare, streamIdForClient, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleLowDataModeEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };