@whereby.com/browser-sdk 3.31.2 → 3.33.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.
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { WherebyClient, CURRENT_SPEAKER_CHANGED, debounce } from '@whereby.com/core';
3
- export { ACCEPTED_FILE_TYPES, MAX_FILES_PER_UPLOAD, MAX_FILE_SIZE, getUsableCameraEffectPresets, isAudioDenoiserSupported } from '@whereby.com/core';
2
+ import { WherebyClient, CURRENT_SPEAKER_CHANGED, debounce, ROOM_INTEGRATION_PICKER_MESSAGES, roomIntegrationPickerUrl } from '@whereby.com/core';
3
+ export { ACCEPTED_FILE_TYPES, MAX_FILES_PER_UPLOAD, MAX_FILE_SIZE, ROOM_INTEGRATION_PICKER_MESSAGES, getUsableCameraEffectPresets, isAudioDenoiserSupported, roomIntegrationContent, roomIntegrationContentTagName, roomIntegrationPickerUrl } from '@whereby.com/core';
4
4
  import runes from 'runes';
5
5
  import * as PopoverPrimitive from '@radix-ui/react-popover';
6
6
 
@@ -151,7 +151,7 @@ const VideoView = React.forwardRef((_a, ref) => {
151
151
  });
152
152
  VideoView.displayName = "VideoView";
153
153
 
154
- const browserSdkVersion = "3.31.2";
154
+ const browserSdkVersion = "3.33.0";
155
155
 
156
156
  const initialState$1 = {
157
157
  chatMessages: [],
@@ -178,10 +178,19 @@ const initialState$1 = {
178
178
  participantsInCurrentGroup: [],
179
179
  broadcastingParticipants: [],
180
180
  },
181
+ isAudioOnlyModeEnabled: false,
181
182
  isCameraEnabled: false,
182
183
  isMicrophoneEnabled: false,
183
184
  localParticipant: undefined,
184
185
  remoteParticipants: [],
186
+ roomIntegrations: {
187
+ hasFetched: false,
188
+ isFetching: false,
189
+ error: null,
190
+ embeddable: [],
191
+ enabled: [],
192
+ running: [],
193
+ },
185
194
  screenshares: [],
186
195
  connectionStatus: "ready",
187
196
  connectionError: null,
@@ -243,6 +252,7 @@ function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectio
243
252
  const toggleMicrophone = React.useCallback((enabled) => client.toggleMicrophone(enabled), [client]);
244
253
  const toggleHdMode = React.useCallback((enabled) => client.toggleHdMode(enabled), [client]);
245
254
  const toggleLowDataMode = React.useCallback((enabled) => client.toggleLowDataMode(enabled), [client]);
255
+ const toggleAudioOnlyMode = React.useCallback((enabled, options) => client.toggleAudioOnlyMode(enabled, options), [client]);
246
256
  const toggleWidescreenMode = React.useCallback((enabled) => client.toggleWidescreenMode(enabled), [client]);
247
257
  const toggleRaiseHand = React.useCallback((enabled) => client.toggleRaiseHand(enabled), [client]);
248
258
  const askToSpeak = React.useCallback((participantId) => client.askToSpeak(participantId), [client]);
@@ -268,6 +278,9 @@ function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectio
268
278
  const endMeeting = React.useCallback((stayBehind) => client.endMeeting(stayBehind), [client]);
269
279
  const joinBreakoutGroup = React.useCallback((group) => client.joinBreakoutGroup(group), [client]);
270
280
  const joinBreakoutMainRoom = React.useCallback(() => client.joinBreakoutMainRoom(), [client]);
281
+ const startRoomIntegration = React.useCallback((options) => client.startRoomIntegration(options), [client]);
282
+ const stopRoomIntegration = React.useCallback((options) => client.stopRoomIntegration(options), [client]);
283
+ const updateRoomIntegrationProps = React.useCallback((options) => client.updateRoomIntegrationProps(options), [client]);
271
284
  const startBreakoutSession = React.useCallback((options) => client.startBreakoutSession(options), [client]);
272
285
  const updateBreakoutSession = React.useCallback((options) => client.updateBreakoutSession(options), [client]);
273
286
  const stopBreakoutSession = React.useCallback(() => client.stopBreakoutSession(), [client]);
@@ -333,11 +346,15 @@ function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectio
333
346
  toggleRaiseHand,
334
347
  toggleHdMode,
335
348
  toggleLowDataMode,
349
+ toggleAudioOnlyMode,
336
350
  toggleWidescreenMode,
337
351
  spotlightParticipant,
338
352
  removeSpotlight,
339
353
  joinBreakoutGroup,
340
354
  joinBreakoutMainRoom,
355
+ startRoomIntegration,
356
+ stopRoomIntegration,
357
+ updateRoomIntegrationProps,
341
358
  startBreakoutSession,
342
359
  updateBreakoutSession,
343
360
  stopBreakoutSession,
@@ -534,6 +551,9 @@ function VideoCell(_a) {
534
551
  React.createElement("div", Object.assign({ style: contentStyle }, rest), React.cloneElement(children, { contentWidth, contentHeight, withRoundedCorners, withShadow }))));
535
552
  }
536
553
 
554
+ const IntegrationCell = ({ cellId, children, isHiddenCell = false }) => (React.createElement("div", { "data-cell-id": cellId, "aria-hidden": isHiddenCell || undefined, style: { height: "100%", width: "100%" } }, children));
555
+ IntegrationCell.displayName = "IntegrationCell";
556
+
537
557
  function generateStylesFromFrame({ origin, bounds }) {
538
558
  return {
539
559
  top: Math.round(origin.top),
@@ -549,7 +569,10 @@ function renderVideoCell({ cell, child, clientId, style = {}, withRoundedCorners
549
569
  function renderSubgridVideoCells({ content, stageLayout, withRoundedCorners, withShadow, }) {
550
570
  const cells = stageLayout.subgrid.cells;
551
571
  return content.map((child, index) => {
552
- var _a, _b, _c, _d;
572
+ var _a, _b, _c, _d, _e;
573
+ if (!child) {
574
+ return null;
575
+ }
553
576
  const cell = cells[index];
554
577
  const style = { height: Math.round(cell.bounds.height), width: Math.round(cell.bounds.width), transform: "" };
555
578
  const origin = {
@@ -557,9 +580,9 @@ function renderSubgridVideoCells({ content, stageLayout, withRoundedCorners, wit
557
580
  left: stageLayout.subgrid.origin.left + cell.origin.left,
558
581
  };
559
582
  style.transform = `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`;
560
- const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
561
- const leftPaddings = ((_c = cell.paddings) === null || _c === void 0 ? void 0 : _c.left) || 0;
562
- const rightPaddings = ((_d = cell.paddings) === null || _d === void 0 ? void 0 : _d.right) || 0;
583
+ const clientId = ((_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.cellId) || ((_c = (_b = child === null || child === void 0 ? void 0 : child.props) === null || _b === void 0 ? void 0 : _b.participant) === null || _c === void 0 ? void 0 : _c.id);
584
+ const leftPaddings = ((_d = cell.paddings) === null || _d === void 0 ? void 0 : _d.left) || 0;
585
+ const rightPaddings = ((_e = cell.paddings) === null || _e === void 0 ? void 0 : _e.right) || 0;
563
586
  const childWithProps = React.cloneElement(child, {
564
587
  avatarSize: cell.bounds.width - leftPaddings - rightPaddings,
565
588
  canZoom: false,
@@ -583,8 +606,8 @@ function renderPresentationGridVideoCells({ content, isConstrained, stageLayout,
583
606
  var _a;
584
607
  const cells = ((_a = stageLayout.presentationGrid) === null || _a === void 0 ? void 0 : _a.cells) || [];
585
608
  return content.map((child, index) => {
586
- var _a, _b;
587
- if (!stageLayout.presentationGrid) {
609
+ var _a, _b, _c, _d;
610
+ if (!child || !stageLayout.presentationGrid) {
588
611
  return null;
589
612
  }
590
613
  const cell = cells[index];
@@ -599,10 +622,10 @@ function renderPresentationGridVideoCells({ content, isConstrained, stageLayout,
599
622
  height: Math.round(cell.bounds.height),
600
623
  transform: `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`,
601
624
  };
602
- const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
625
+ const clientId = ((_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.cellId) || ((_c = (_b = child === null || child === void 0 ? void 0 : child.props) === null || _b === void 0 ? void 0 : _b.participant) === null || _c === void 0 ? void 0 : _c.id);
603
626
  const childWithProps = React.cloneElement(child, {
604
627
  isSmallCell: cell.isSmallCell,
605
- isZoomedByDefault: !!isConstrained && !(child === null || child === void 0 ? void 0 : child.props.participant.isPresentation),
628
+ isZoomedByDefault: !!isConstrained && !!((_d = child === null || child === void 0 ? void 0 : child.props) === null || _d === void 0 ? void 0 : _d.participant) && !child.props.participant.isPresentation,
606
629
  canZoom: !!isConstrained,
607
630
  key: clientId || `presentation-${index}`,
608
631
  });
@@ -620,8 +643,8 @@ function renderGridVideoCells({ content, isConstrained, stageLayout, withRounded
620
643
  var _a;
621
644
  const cells = ((_a = stageLayout.videoGrid) === null || _a === void 0 ? void 0 : _a.cells) || [];
622
645
  const gridVideoCells = content.map((child, index) => {
623
- var _a, _b;
624
- if (!stageLayout.videoGrid) {
646
+ var _a, _b, _c, _d;
647
+ if (!child || !stageLayout.videoGrid) {
625
648
  return null;
626
649
  }
627
650
  const cell = cells[index];
@@ -634,10 +657,10 @@ function renderGridVideoCells({ content, isConstrained, stageLayout, withRounded
634
657
  height: Math.round(cell.bounds.height),
635
658
  transform: `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`,
636
659
  };
637
- const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
660
+ const clientId = ((_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.cellId) || ((_c = (_b = child === null || child === void 0 ? void 0 : child.props) === null || _b === void 0 ? void 0 : _b.participant) === null || _c === void 0 ? void 0 : _c.id);
638
661
  const childWithProps = React.cloneElement(child, {
639
662
  isSmallCell: cell.isSmallCell,
640
- isZoomedByDefault: !!isConstrained && !(child === null || child === void 0 ? void 0 : child.props.participant.isPresentation),
663
+ isZoomedByDefault: !!isConstrained && !!((_d = child === null || child === void 0 ? void 0 : child.props) === null || _d === void 0 ? void 0 : _d.participant) && !child.props.participant.isPresentation,
641
664
  canZoom: !!isConstrained,
642
665
  key: clientId || `video-${index}`,
643
666
  });
@@ -685,7 +708,33 @@ function renderFloatingVideoCell({ content, containerFrame, stageLayout, withRou
685
708
  withShadow: false,
686
709
  });
687
710
  }
688
- function VideoStageLayout({ containerFrame, debug = false, featureRoundedCornersOff = false, floatingContent, gridContent = [], isConstrained = false, layoutOverflowBackdropFrame = makeFrame(), layoutVideoStage: stageLayout, presentationGridContent = [], subgridContent = [], }) {
711
+ const HIDDEN_CELL_STYLE = {
712
+ width: 1,
713
+ height: 1,
714
+ opacity: 0,
715
+ overflow: "hidden",
716
+ pointerEvents: "none",
717
+ };
718
+ function renderHiddenCells({ content }) {
719
+ const cell = { bounds: { width: 1, height: 1 }, aspectRatio: 1 };
720
+ return content.map((child, index) => {
721
+ var _a;
722
+ if (!child) {
723
+ return null;
724
+ }
725
+ const clientId = ((_a = child.props) === null || _a === void 0 ? void 0 : _a.cellId) || `hidden-${index}`;
726
+ const childWithProps = React.cloneElement(child, { isHiddenCell: true, key: clientId });
727
+ return renderVideoCell({ cell, child: childWithProps, clientId, style: HIDDEN_CELL_STYLE });
728
+ });
729
+ }
730
+ function withIntegrationCellsFirst(cells) {
731
+ const isIntegrationCell = (cell) => { var _a, _b; return ((_b = (_a = cell === null || cell === void 0 ? void 0 : cell.props) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b.type) === IntegrationCell; };
732
+ const integrationCells = cells
733
+ .filter(isIntegrationCell)
734
+ .sort((a, b) => String(a === null || a === void 0 ? void 0 : a.key).localeCompare(String(b === null || b === void 0 ? void 0 : b.key)));
735
+ return [...integrationCells, ...cells.filter((cell) => !isIntegrationCell(cell))];
736
+ }
737
+ function VideoStageLayout({ containerFrame, debug = false, featureRoundedCornersOff = false, floatingContent, gridContent = [], isConstrained = false, layoutOverflowBackdropFrame = makeFrame(), layoutVideoStage: stageLayout, presentationGridContent = [], subgridContent = [], hiddenContent = [], }) {
689
738
  const withRoundedCorners = !featureRoundedCornersOff && !isConstrained;
690
739
  const cells = [];
691
740
  if (gridContent.length) {
@@ -722,16 +771,221 @@ function VideoStageLayout({ containerFrame, debug = false, featureRoundedCorners
722
771
  withShadow: true,
723
772
  }));
724
773
  }
774
+ if (hiddenContent.length) {
775
+ cells.push(...renderHiddenCells({ content: hiddenContent }));
776
+ }
725
777
  return (React.createElement("div", { key: "video-stage-layout", style: {
726
778
  width: "100%",
727
779
  height: "100%",
728
780
  position: "relative",
729
781
  } },
730
782
  hasBounds(layoutOverflowBackdropFrame.bounds) && (React.createElement("div", { style: generateStylesFromFrame(layoutOverflowBackdropFrame) })),
731
- cells,
783
+ withIntegrationCellsFirst(cells),
732
784
  debug && (React.createElement(React.Fragment, null))));
733
785
  }
734
786
 
787
+ function useIframeContract(ref, hookName, enabled) {
788
+ React.useEffect(() => {
789
+ if (!enabled) {
790
+ return;
791
+ }
792
+ const timer = setTimeout(() => {
793
+ const frame = ref.current;
794
+ if (!frame) {
795
+ console.warn(`${hookName}: iframeProps.ref was never attached. Spread iframeProps onto the ` +
796
+ `<iframe> you render — without the ref the hook cannot tell which window is ` +
797
+ `answering, so nothing will ever arrive.`);
798
+ return;
799
+ }
800
+ if (frame.hasAttribute("sandbox")) {
801
+ console.warn(`${hookName}: the iframe has a sandbox attribute. Some integrations open their ` +
802
+ `own window to sign in, which a sandbox without allow-popups blocks silently.`);
803
+ }
804
+ }, 0);
805
+ return () => clearTimeout(timer);
806
+ }, [ref, hookName, enabled]);
807
+ }
808
+
809
+ const HOST_TO_FRAME = {
810
+ PROPS: "whereby:props",
811
+ SET_VOLUME: "whereby:setVolume",
812
+ GET_VOLUME: "whereby:getVolume",
813
+ };
814
+ const FRAME_TO_HOST = {
815
+ FRAME_READY: "whereby:frameReady",
816
+ UPDATE_PROPS: "whereby:updateProps",
817
+ CONTENT_READY: "whereby:contentReady",
818
+ CLOSE: "whereby:close",
819
+ AUDIO_OVERRIDE: "whereby:audioOverride",
820
+ VOLUME: "whereby:volume",
821
+ };
822
+ const DEFAULT_ALLOW$1 = "autoplay; fullscreen; encrypted-media; picture-in-picture";
823
+ const VOLUME_REQUEST_TIMEOUT = 2000;
824
+ function outboundProps(session) {
825
+ return Object.assign(Object.assign({}, session.props), { ispresenter: session.isPresenter, presenterdisplayname: session.presenterDisplayName, roomintegrationsessionid: session.roomIntegrationSessionId, breakoutgroupid: session.breakoutGroupId });
826
+ }
827
+ function diffProps(next, previous) {
828
+ if (!previous) {
829
+ return next;
830
+ }
831
+ return Object.keys(next).reduce((patch, key) => {
832
+ if (previous[key] !== next[key]) {
833
+ patch[key] = next[key];
834
+ }
835
+ return patch;
836
+ }, {});
837
+ }
838
+ function useRoomIntegrationView({ session, onAudioOverride, onContentReady, allow = DEFAULT_ALLOW$1, title, }) {
839
+ var _a;
840
+ const client = (_a = React.useContext(WherebyContext)) === null || _a === void 0 ? void 0 : _a.getRoomConnection();
841
+ const iframeRef = React.useRef(null);
842
+ const lastSentRef = React.useRef(null);
843
+ const isFrameReadyRef = React.useRef(false);
844
+ const volumeRequestsRef = React.useRef(new Map());
845
+ const nextRequestIdRef = React.useRef(0);
846
+ const sessionRef = React.useRef(session);
847
+ const onAudioOverrideRef = React.useRef(onAudioOverride);
848
+ const onContentReadyRef = React.useRef(onContentReady);
849
+ sessionRef.current = session;
850
+ onAudioOverrideRef.current = onAudioOverride;
851
+ onContentReadyRef.current = onContentReady;
852
+ const { webview } = session.integration;
853
+ const { roomIntegrationSessionId } = session;
854
+ const frameOrigin = React.useMemo(() => {
855
+ try {
856
+ return new URL(webview).origin;
857
+ }
858
+ catch (_a) {
859
+ console.warn(`useRoomIntegrationView: unusable webview url ${webview}`);
860
+ return null;
861
+ }
862
+ }, [webview]);
863
+ const src = React.useMemo(() => {
864
+ if (!frameOrigin) {
865
+ return null;
866
+ }
867
+ const url = new URL(webview);
868
+ url.searchParams.set("parentOrigin", window.location.origin);
869
+ url.searchParams.set("roomintegrationsessionid", roomIntegrationSessionId);
870
+ return url.href;
871
+ }, [webview, frameOrigin, roomIntegrationSessionId]);
872
+ const post = React.useCallback((type, payload) => {
873
+ var _a;
874
+ const frameWindow = (_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow;
875
+ if (!frameWindow || !frameOrigin) {
876
+ return;
877
+ }
878
+ frameWindow.postMessage({ type, payload }, frameOrigin);
879
+ }, [frameOrigin]);
880
+ React.useEffect(() => {
881
+ isFrameReadyRef.current = false;
882
+ lastSentRef.current = null;
883
+ }, [src]);
884
+ React.useEffect(() => {
885
+ if (!frameOrigin) {
886
+ return;
887
+ }
888
+ const handleMessage = (event) => {
889
+ var _a, _b, _c, _d;
890
+ if (event.origin !== frameOrigin || event.source !== ((_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow)) {
891
+ return;
892
+ }
893
+ const { type, payload } = event.data || {};
894
+ const currentSession = sessionRef.current;
895
+ switch (type) {
896
+ case FRAME_TO_HOST.FRAME_READY: {
897
+ const props = outboundProps(currentSession);
898
+ isFrameReadyRef.current = true;
899
+ lastSentRef.current = props;
900
+ post(HOST_TO_FRAME.PROPS, props);
901
+ break;
902
+ }
903
+ case FRAME_TO_HOST.UPDATE_PROPS: {
904
+ if (payload === null || payload === void 0 ? void 0 : payload.props) {
905
+ client === null || client === void 0 ? void 0 : client.updateRoomIntegrationProps({
906
+ roomIntegrationSessionId: currentSession.roomIntegrationSessionId,
907
+ props: payload.props,
908
+ });
909
+ }
910
+ break;
911
+ }
912
+ case FRAME_TO_HOST.CLOSE: {
913
+ if (currentSession.canStop) {
914
+ client === null || client === void 0 ? void 0 : client.stopRoomIntegration({
915
+ roomIntegrationSessionId: currentSession.roomIntegrationSessionId,
916
+ intent: "end",
917
+ });
918
+ }
919
+ break;
920
+ }
921
+ case FRAME_TO_HOST.AUDIO_OVERRIDE: {
922
+ (_b = onAudioOverrideRef.current) === null || _b === void 0 ? void 0 : _b.call(onAudioOverrideRef, (_c = payload === null || payload === void 0 ? void 0 : payload.enabled) !== null && _c !== void 0 ? _c : null);
923
+ break;
924
+ }
925
+ case FRAME_TO_HOST.CONTENT_READY: {
926
+ (_d = onContentReadyRef.current) === null || _d === void 0 ? void 0 : _d.call(onContentReadyRef);
927
+ break;
928
+ }
929
+ case FRAME_TO_HOST.VOLUME: {
930
+ const resolve = volumeRequestsRef.current.get(payload === null || payload === void 0 ? void 0 : payload.requestId);
931
+ if (resolve) {
932
+ volumeRequestsRef.current.delete(payload.requestId);
933
+ resolve(payload.volume);
934
+ }
935
+ break;
936
+ }
937
+ }
938
+ };
939
+ window.addEventListener("message", handleMessage);
940
+ return () => window.removeEventListener("message", handleMessage);
941
+ }, [frameOrigin, client, post]);
942
+ const serializedProps = JSON.stringify(outboundProps(session));
943
+ React.useEffect(() => {
944
+ if (!isFrameReadyRef.current) {
945
+ return;
946
+ }
947
+ const props = outboundProps(sessionRef.current);
948
+ const patch = diffProps(props, lastSentRef.current);
949
+ lastSentRef.current = props;
950
+ if (Object.keys(patch).length) {
951
+ post(HOST_TO_FRAME.PROPS, patch);
952
+ }
953
+ }, [serializedProps, post]);
954
+ const setVolume = React.useCallback((volume) => post(HOST_TO_FRAME.SET_VOLUME, { volume }), [post]);
955
+ const getVolume = React.useCallback(() => new Promise((resolve, reject) => {
956
+ const requestId = ++nextRequestIdRef.current;
957
+ const requests = volumeRequestsRef.current;
958
+ const timer = setTimeout(() => {
959
+ requests.delete(requestId);
960
+ reject(new Error("Timed out reading volume from the room integration"));
961
+ }, VOLUME_REQUEST_TIMEOUT);
962
+ requests.set(requestId, (volume) => {
963
+ clearTimeout(timer);
964
+ resolve(volume);
965
+ });
966
+ post(HOST_TO_FRAME.GET_VOLUME, { requestId });
967
+ }), [post]);
968
+ useIframeContract(iframeRef, "useRoomIntegrationView", !!src);
969
+ return {
970
+ iframeProps: src
971
+ ? {
972
+ ref: iframeRef,
973
+ src,
974
+ title: title || session.integration.title,
975
+ allow,
976
+ }
977
+ : null,
978
+ getVolume,
979
+ setVolume,
980
+ };
981
+ }
982
+
983
+ const IFRAME_STYLE = { border: "none", width: "100%", height: "100%" };
984
+ function DefaultIntegrationView({ session }) {
985
+ const { iframeProps } = useRoomIntegrationView({ session });
986
+ return iframeProps ? React.createElement("iframe", Object.assign({}, iframeProps, { allowFullScreen: true, style: IFRAME_STYLE })) : null;
987
+ }
988
+
735
989
  const WIDE_AR = 16 / 9;
736
990
  const NORMAL_AR = 4 / 3;
737
991
  const clamp = ({ value, min, max }) => Math.min(Math.max(value, min), max);
@@ -1787,6 +2041,7 @@ function makeVideoCellView({ aspectRatio, avatarSize, cellPaddings, client = und
1787
2041
  avatarSize,
1788
2042
  cellPaddings,
1789
2043
  client,
2044
+ cellId: (client === null || client === void 0 ? void 0 : client.id) || "",
1790
2045
  clientId: (client === null || client === void 0 ? void 0 : client.id) || "",
1791
2046
  isDraggable,
1792
2047
  isPlaceholder,
@@ -1794,20 +2049,38 @@ function makeVideoCellView({ aspectRatio, avatarSize, cellPaddings, client = und
1794
2049
  type: "video",
1795
2050
  };
1796
2051
  }
2052
+ function integrationAspectRatio(session) {
2053
+ var _a;
2054
+ const reported = Number((_a = session.props) === null || _a === void 0 ? void 0 : _a.aspectratio);
2055
+ return isFinite(reported) && reported > 0 ? reported : undefined;
2056
+ }
2057
+ function roomIntegrationCellId(session) {
2058
+ return `room-integration:${session.roomIntegrationSessionId}`;
2059
+ }
2060
+ function makeIntegrationCellView({ session, isDraggable = false, isSubgrid = false, }) {
2061
+ const cellId = roomIntegrationCellId(session);
2062
+ return {
2063
+ aspectRatio: integrationAspectRatio(session) || 16 / 9,
2064
+ cellId,
2065
+ clientId: cellId,
2066
+ isDraggable,
2067
+ isSubgrid,
2068
+ session,
2069
+ type: "integration",
2070
+ };
2071
+ }
1797
2072
 
1798
2073
  const STAGE_PARTICIPANT_LIMIT = 12;
1799
2074
  const ACTIVE_VIDEO_SUBGRID_TRIGGER = 12;
1800
2075
  const ACTIVE_VIDEOS_PHONE_LIMIT = 24;
1801
2076
 
1802
- function calculateSubgridViews({ clientViews, activeVideosSubgridTrigger, shouldShowSubgrid, spotlightedParticipants, maximizedParticipant, isPhoneResolution, }) {
2077
+ function calculateSubgridViews({ clientViews, activeVideosSubgridTrigger, shouldShowSubgrid, spotlightedParticipants, maximizedCellId, hasStagedIntegration = false, isPhoneResolution, }) {
1803
2078
  if (!shouldShowSubgrid) {
1804
2079
  return [];
1805
2080
  }
1806
2081
  const hasSpotlights = spotlightedParticipants.length > 0;
1807
- const hasPresentationStage = hasSpotlights;
1808
- const notMaximized = maximizedParticipant
1809
- ? clientViews.filter((client) => client.id !== maximizedParticipant.id)
1810
- : clientViews;
2082
+ const hasPresentationStage = hasSpotlights || hasStagedIntegration;
2083
+ const notMaximized = maximizedCellId ? clientViews.filter((client) => client.id !== maximizedCellId) : clientViews;
1811
2084
  const notSpotlighted = notMaximized.filter((client) => !client.isPresentation && !spotlightedParticipants.includes(client));
1812
2085
  const noVideoViews = notSpotlighted.filter((client) => !client.isVideoEnabled);
1813
2086
  const videoLimitReached = notSpotlighted.filter((client) => client.isVideoEnabled).length > activeVideosSubgridTrigger;
@@ -1838,7 +2111,8 @@ function calculateSubgridViews({ clientViews, activeVideosSubgridTrigger, should
1838
2111
  }
1839
2112
  return noVideoViews;
1840
2113
  }
1841
- function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID_TRIGGER, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, forceSubgrid = true, enableSubgrid = true, maximizedParticipant, floatingParticipant, isConstrained = false, } = {}) {
2114
+ const NO_INTEGRATIONS = [];
2115
+ function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID_TRIGGER, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, forceSubgrid = true, enableSubgrid = true, maximizedCellId, floatingCellId, isConstrained = false, includeIntegrations = false, } = {}) {
1842
2116
  var _a;
1843
2117
  const client = (_a = React.useContext(WherebyContext)) === null || _a === void 0 ? void 0 : _a.getGrid();
1844
2118
  if (!client) {
@@ -1851,6 +2125,9 @@ function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID
1851
2125
  const handleSpotlightedParticipantsChanged = React.useCallback((spotlighted) => {
1852
2126
  setState((prevState) => (Object.assign(Object.assign({}, prevState), { spotlightedParticipants: spotlighted })));
1853
2127
  }, [setState]);
2128
+ const handleRunningRoomIntegrationsChanged = React.useCallback((runningRoomIntegrations) => {
2129
+ setState((prevState) => (Object.assign(Object.assign({}, prevState), { runningRoomIntegrations })));
2130
+ }, [setState]);
1854
2131
  const handleNumParticipantsChanged = React.useCallback((num) => {
1855
2132
  setState((prevState) => (Object.assign(Object.assign({}, prevState), { numParticipants: num })));
1856
2133
  }, [setState]);
@@ -1858,19 +2135,21 @@ function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID
1858
2135
  const unsubscribeClientViews = client.subscribeClientViews(handleClientViewChanged);
1859
2136
  const unsubscribeSpotlighted = client.subscribeSpotlightedParticipants(handleSpotlightedParticipantsChanged);
1860
2137
  const unsubscribeNumParticipants = client.subscribeNumberOfClientViews(handleNumParticipantsChanged);
2138
+ const unsubscribeRunningRoomIntegrations = client.subscribeRunningRoomIntegrations(handleRunningRoomIntegrationsChanged);
1861
2139
  setState(client.getState());
1862
2140
  return () => {
1863
2141
  unsubscribeClientViews();
1864
2142
  unsubscribeSpotlighted();
1865
2143
  unsubscribeNumParticipants();
2144
+ unsubscribeRunningRoomIntegrations();
1866
2145
  };
1867
2146
  }, [client]);
1868
2147
  const allClientViews = React.useMemo(() => state.allClientViews, [state.allClientViews]);
1869
2148
  const spotlightedParticipants = React.useMemo(() => state.spotlightedParticipants, [state.spotlightedParticipants]);
1870
2149
  const numParticipants = React.useMemo(() => state.numParticipants, [state.numParticipants]);
1871
- const floatingClientView = React.useMemo(() => {
1872
- return floatingParticipant;
1873
- }, [floatingParticipant]);
2150
+ const runningRoomIntegrations = React.useMemo(() => (includeIntegrations ? state.runningRoomIntegrations : NO_INTEGRATIONS), [includeIntegrations, state.runningRoomIntegrations]);
2151
+ const maximizedClientView = React.useMemo(() => (maximizedCellId ? allClientViews.find((client) => client.id === maximizedCellId) || null : null), [allClientViews, maximizedCellId]);
2152
+ const floatingClientView = React.useMemo(() => (floatingCellId ? allClientViews.find((client) => client.id === floatingCellId) || null : null), [allClientViews, floatingCellId]);
1874
2153
  const clientViewsNotFloating = React.useMemo(() => {
1875
2154
  if (floatingClientView) {
1876
2155
  return allClientViews.filter((c) => c.id !== floatingClientView.id);
@@ -1883,51 +2162,84 @@ function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID
1883
2162
  }
1884
2163
  return forceSubgrid ? true : numParticipants > stageParticipantLimit;
1885
2164
  }, [forceSubgrid, numParticipants, stageParticipantLimit, enableSubgrid]);
2165
+ const integrationsInPresentationGrid = React.useMemo(() => {
2166
+ if (!runningRoomIntegrations.length || maximizedClientView) {
2167
+ return [];
2168
+ }
2169
+ return [runningRoomIntegrations[0]];
2170
+ }, [runningRoomIntegrations, maximizedClientView]);
2171
+ const integrationsInSubgrid = React.useMemo(() => {
2172
+ if (!shouldShowSubgrid) {
2173
+ return [];
2174
+ }
2175
+ return runningRoomIntegrations.filter((session) => !integrationsInPresentationGrid.some((staged) => staged.roomIntegrationSessionId === session.roomIntegrationSessionId));
2176
+ }, [runningRoomIntegrations, integrationsInPresentationGrid, shouldShowSubgrid]);
2177
+ const integrationsHidden = React.useMemo(() => {
2178
+ const visible = [...integrationsInPresentationGrid, ...integrationsInSubgrid];
2179
+ return runningRoomIntegrations.filter((session) => !visible.some((shown) => shown.roomIntegrationSessionId === session.roomIntegrationSessionId));
2180
+ }, [runningRoomIntegrations, integrationsInPresentationGrid, integrationsInSubgrid]);
1886
2181
  const clientViewsInSubgrid = React.useMemo(() => {
1887
2182
  return calculateSubgridViews({
1888
2183
  clientViews: clientViewsNotFloating,
1889
2184
  activeVideosSubgridTrigger,
1890
2185
  shouldShowSubgrid,
1891
2186
  spotlightedParticipants,
1892
- maximizedParticipant,
2187
+ maximizedCellId,
2188
+ hasStagedIntegration: integrationsInPresentationGrid.length > 0,
1893
2189
  isPhoneResolution: isConstrained,
1894
2190
  });
1895
- }, [clientViewsNotFloating, shouldShowSubgrid, activeVideosSubgridTrigger, spotlightedParticipants, isConstrained]);
2191
+ }, [
2192
+ clientViewsNotFloating,
2193
+ shouldShowSubgrid,
2194
+ activeVideosSubgridTrigger,
2195
+ spotlightedParticipants,
2196
+ maximizedCellId,
2197
+ integrationsInPresentationGrid,
2198
+ isConstrained,
2199
+ ]);
1896
2200
  const clientViewsOnStage = React.useMemo(() => {
1897
2201
  return clientViewsNotFloating.filter((client) => !clientViewsInSubgrid.includes(client));
1898
2202
  }, [clientViewsNotFloating, clientViewsInSubgrid]);
1899
2203
  const clientViewsInPresentationGrid = React.useMemo(() => {
1900
- if (maximizedParticipant) {
1901
- return [maximizedParticipant];
2204
+ if (maximizedClientView) {
2205
+ return [maximizedClientView];
2206
+ }
2207
+ if (integrationsInPresentationGrid.length) {
2208
+ return [];
1902
2209
  }
1903
- return spotlightedParticipants;
1904
- }, [spotlightedParticipants, maximizedParticipant]);
2210
+ return spotlightedParticipants.filter((client) => clientViewsOnStage.includes(client));
2211
+ }, [spotlightedParticipants, maximizedClientView, integrationsInPresentationGrid, clientViewsOnStage]);
1905
2212
  const clientViewsInGrid = React.useMemo(() => {
1906
2213
  return clientViewsOnStage.filter((client) => !clientViewsInPresentationGrid.includes(client));
1907
2214
  }, [clientViewsOnStage, clientViewsInPresentationGrid]);
1908
2215
  return {
1909
2216
  floatingClientView,
2217
+ maximizedClientView,
1910
2218
  clientViewsInGrid,
1911
2219
  clientViewsInPresentationGrid,
1912
2220
  clientViewsInSubgrid,
2221
+ integrationsInPresentationGrid,
2222
+ integrationsInSubgrid,
2223
+ integrationsHidden,
1913
2224
  spotlightedParticipants,
1914
2225
  };
1915
2226
  }
1916
2227
 
1917
- function useGrid({ activeVideosSubgridTrigger, forceSubgrid, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, gridGap = 8, videoGridGap = 8, enableSubgrid = true, enableConstrainedGrid = true, } = {}) {
2228
+ function useGrid({ activeVideosSubgridTrigger, forceSubgrid, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, gridGap = 8, videoGridGap = 8, enableSubgrid = true, enableConstrainedGrid = true, includeIntegrations = false, } = {}) {
1918
2229
  const [containerBounds, setContainerBounds] = React.useState({ width: 0, height: 0 });
1919
2230
  const [isConstrained, setIsConstrained] = React.useState(false);
1920
2231
  const [clientAspectRatios, setClientAspectRatios] = React.useState({});
1921
- const [maximizedParticipant, setMaximizedParticipant] = React.useState(null);
1922
- const [floatingParticipant, setFloatingParticipant] = React.useState(null);
1923
- const { clientViewsInGrid, clientViewsInPresentationGrid, clientViewsInSubgrid, floatingClientView } = useGridParticipants({
2232
+ const [maximizedCellId, setMaximizedCellId] = React.useState(null);
2233
+ const [floatingCellId, setFloatingCellId] = React.useState(null);
2234
+ const { clientViewsInGrid, clientViewsInPresentationGrid, clientViewsInSubgrid, floatingClientView, maximizedClientView, integrationsInPresentationGrid, integrationsInSubgrid, integrationsHidden, } = useGridParticipants({
1924
2235
  activeVideosSubgridTrigger,
1925
2236
  forceSubgrid,
1926
2237
  stageParticipantLimit,
1927
2238
  enableSubgrid,
1928
- maximizedParticipant,
1929
- floatingParticipant,
2239
+ maximizedCellId,
2240
+ floatingCellId,
1930
2241
  isConstrained: !!enableConstrainedGrid && !!isConstrained,
2242
+ includeIntegrations,
1931
2243
  });
1932
2244
  const cellViewsFloating = React.useMemo(() => {
1933
2245
  return floatingClientView
@@ -1952,26 +2264,35 @@ function useGrid({ activeVideosSubgridTrigger, forceSubgrid, stageParticipantLim
1952
2264
  });
1953
2265
  }, [clientViewsInGrid, clientAspectRatios]);
1954
2266
  const cellViewsInPresentationGrid = React.useMemo(() => {
1955
- return clientViewsInPresentationGrid.map((client) => {
1956
- return makeVideoCellView({
1957
- client,
1958
- aspectRatio: clientAspectRatios[client.id],
1959
- avatarSize: 0,
1960
- cellPaddings: { top: 0, right: 0 },
1961
- });
1962
- });
1963
- }, [clientViewsInPresentationGrid, clientAspectRatios]);
2267
+ return [
2268
+ ...integrationsInPresentationGrid.map((session) => makeIntegrationCellView({ session })),
2269
+ ...clientViewsInPresentationGrid.map((client) => {
2270
+ return makeVideoCellView({
2271
+ client,
2272
+ aspectRatio: clientAspectRatios[client.id],
2273
+ avatarSize: 0,
2274
+ cellPaddings: { top: 0, right: 0 },
2275
+ });
2276
+ }),
2277
+ ];
2278
+ }, [clientViewsInPresentationGrid, integrationsInPresentationGrid, clientAspectRatios]);
1964
2279
  const cellViewsInSubgrid = React.useMemo(() => {
1965
- return clientViewsInSubgrid.map((client) => {
1966
- return makeVideoCellView({
1967
- client,
1968
- aspectRatio: clientAspectRatios[client.id],
1969
- avatarSize: 0,
1970
- cellPaddings: { top: 0, right: 0 },
1971
- isSubgrid: true,
1972
- });
1973
- });
1974
- }, [clientViewsInSubgrid, clientAspectRatios]);
2280
+ return [
2281
+ ...clientViewsInSubgrid.map((client) => {
2282
+ return makeVideoCellView({
2283
+ client,
2284
+ aspectRatio: clientAspectRatios[client.id],
2285
+ avatarSize: 0,
2286
+ cellPaddings: { top: 0, right: 0 },
2287
+ isSubgrid: true,
2288
+ });
2289
+ }),
2290
+ ...integrationsInSubgrid.map((session) => makeIntegrationCellView({ session, isSubgrid: true })),
2291
+ ];
2292
+ }, [clientViewsInSubgrid, integrationsInSubgrid, clientAspectRatios]);
2293
+ const cellViewsHidden = React.useMemo(() => {
2294
+ return integrationsHidden.map((session) => makeIntegrationCellView({ session }));
2295
+ }, [integrationsHidden]);
1975
2296
  const containerFrame = React.useMemo(() => {
1976
2297
  return makeFrame(containerBounds);
1977
2298
  }, [containerBounds]);
@@ -2008,14 +2329,17 @@ function useGrid({ activeVideosSubgridTrigger, forceSubgrid, stageParticipantLim
2008
2329
  cellViewsVideoGrid,
2009
2330
  cellViewsInPresentationGrid,
2010
2331
  cellViewsInSubgrid,
2332
+ cellViewsHidden,
2011
2333
  clientAspectRatios,
2012
2334
  videoStage,
2013
2335
  setContainerBounds,
2014
2336
  setClientAspectRatios,
2015
- maximizedParticipant,
2016
- setMaximizedParticipant,
2017
- floatingParticipant,
2018
- setFloatingParticipant,
2337
+ maximizedCellId,
2338
+ setMaximizedCellId,
2339
+ maximizedParticipant: maximizedClientView,
2340
+ floatingCellId,
2341
+ setFloatingCellId,
2342
+ floatingParticipant: floatingClientView,
2019
2343
  isConstrained,
2020
2344
  };
2021
2345
  }
@@ -2129,22 +2453,17 @@ ParticipantMenuTrigger.displayName = PopoverTrigger.displayName;
2129
2453
  const ParticipantMenuItem = React.forwardRef((_a, ref) => {
2130
2454
  var _b;
2131
2455
  var { children, style, participantAction } = _a, props = __rest(_a, ["children", "style", "participantAction"]);
2132
- const { participant, setOpen, maximizedParticipant, setMaximizedParticipant, setFloatingParticipant, floatingParticipant, } = useParticipantMenu();
2456
+ const { participant, setOpen, maximizedCellId, setMaximizedCellId, floatingCellId, setFloatingCellId } = useParticipantMenu();
2133
2457
  const client = (_b = React.useContext(WherebyContext)) === null || _b === void 0 ? void 0 : _b.getGrid();
2134
2458
  const { spotlightedParticipants } = useGridParticipants();
2135
2459
  const isSpotlighted = spotlightedParticipants.find((p) => p.id === participant.id);
2136
- const isMaximized = (maximizedParticipant === null || maximizedParticipant === void 0 ? void 0 : maximizedParticipant.id) === participant.id;
2137
- const isFloating = (floatingParticipant === null || floatingParticipant === void 0 ? void 0 : floatingParticipant.id) === participant.id;
2460
+ const isMaximized = maximizedCellId === participant.id;
2461
+ const isFloating = floatingCellId === participant.id;
2138
2462
  let onClick;
2139
2463
  switch (participantAction) {
2140
2464
  case "maximize":
2141
2465
  onClick = () => {
2142
- if (isMaximized) {
2143
- setMaximizedParticipant(null);
2144
- }
2145
- else {
2146
- setMaximizedParticipant(participant);
2147
- }
2466
+ setMaximizedCellId(isMaximized ? null : participant.id);
2148
2467
  setOpen(false);
2149
2468
  };
2150
2469
  break;
@@ -2161,12 +2480,7 @@ const ParticipantMenuItem = React.forwardRef((_a, ref) => {
2161
2480
  break;
2162
2481
  case "float":
2163
2482
  onClick = () => {
2164
- if (isFloating) {
2165
- setFloatingParticipant(null);
2166
- }
2167
- else {
2168
- setFloatingParticipant(participant);
2169
- }
2483
+ setFloatingCellId(isFloating ? null : participant.id);
2170
2484
  setOpen(false);
2171
2485
  };
2172
2486
  break;
@@ -2283,28 +2597,35 @@ const GridVideoView = React.forwardRef((_a, ref) => {
2283
2597
  return (React.createElement(VideoView, Object.assign({ ref: videoEl, style: Object.assign(Object.assign({ borderRadius: isConstrained ? 0 : "8px" }, (isConstrained ? { objectFit: "cover" } : {})), style), muted: participant.isLocalClient }, rest, { stream: s, onVideoResize: handleResize })));
2284
2598
  });
2285
2599
  GridVideoView.displayName = "GridVideoView";
2286
- function renderCellView({ cellView, enableParticipantMenu, render }) {
2287
- const participant = cellView === null || cellView === void 0 ? void 0 : cellView.client;
2288
- if (!participant) {
2600
+ function renderCellView({ cellView, enableParticipantMenu, render, renderIntegration }) {
2601
+ if (!cellView) {
2289
2602
  return undefined;
2290
2603
  }
2291
2604
  switch (cellView.type) {
2292
- case "video":
2293
- return (React.createElement(GridCell, { participant: participant },
2605
+ case "video": {
2606
+ const participant = cellView.client;
2607
+ if (!participant) {
2608
+ return undefined;
2609
+ }
2610
+ return (React.createElement(GridCell, { participant: participant, cellId: cellView.cellId },
2294
2611
  React.createElement(React.Fragment, null, render ? (render({ participant })) : (React.createElement(React.Fragment, null,
2295
2612
  React.createElement(GridVideoView, null),
2296
2613
  enableParticipantMenu ? React.createElement(DefaultParticipantMenu, { participant: participant }) : null)))));
2614
+ }
2615
+ case "integration":
2616
+ return (React.createElement(IntegrationCell, { cellId: cellView.cellId }, renderIntegration ? (renderIntegration({ session: cellView.session })) : (React.createElement(DefaultIntegrationView, { session: cellView.session }))));
2297
2617
  }
2298
2618
  }
2299
- function Grid({ renderParticipant, renderSubgridParticipant, renderFloatingParticipant, stageParticipantLimit, gridGap, videoGridGap, enableSubgrid, enableParticipantMenu, enableConstrainedGrid, }) {
2619
+ function Grid({ renderParticipant, renderSubgridParticipant, renderFloatingParticipant, renderIntegration, stageParticipantLimit, gridGap, videoGridGap, enableSubgrid, enableParticipantMenu, enableConstrainedGrid, enableIntegrations = true, }) {
2300
2620
  const gridRef = React.useRef(null);
2301
- const { containerFrame, cellViewsFloating, cellViewsVideoGrid, cellViewsInPresentationGrid, cellViewsInSubgrid, clientAspectRatios, videoStage, setContainerBounds, setClientAspectRatios, maximizedParticipant, setMaximizedParticipant, isConstrained, floatingParticipant, setFloatingParticipant, } = useGrid({
2621
+ const { containerFrame, cellViewsFloating, cellViewsVideoGrid, cellViewsInPresentationGrid, cellViewsInSubgrid, cellViewsHidden, clientAspectRatios, videoStage, setContainerBounds, setClientAspectRatios, maximizedCellId, setMaximizedCellId, maximizedParticipant, isConstrained, floatingCellId, setFloatingCellId, floatingParticipant, } = useGrid({
2302
2622
  activeVideosSubgridTrigger: 12,
2303
2623
  stageParticipantLimit,
2304
2624
  gridGap,
2305
2625
  videoGridGap,
2306
2626
  enableSubgrid,
2307
2627
  enableConstrainedGrid,
2628
+ includeIntegrations: enableIntegrations,
2308
2629
  });
2309
2630
  const handleSetClientAspectRatio = React.useCallback(({ aspectRatio, clientId }) => {
2310
2631
  setClientAspectRatios((prev) => (Object.assign(Object.assign({}, prev), { [clientId]: aspectRatio })));
@@ -2315,13 +2636,16 @@ function Grid({ renderParticipant, renderSubgridParticipant, renderFloatingParti
2315
2636
  : {})));
2316
2637
  }, [cellViewsFloating]);
2317
2638
  const presentationGridContent = React.useMemo(() => cellViewsInPresentationGrid.map((cellView) => renderCellView(Object.assign({ cellView,
2318
- enableParticipantMenu }, (renderParticipant ? { render: ({ participant }) => renderParticipant({ participant }) } : {})))), [cellViewsInPresentationGrid]);
2639
+ enableParticipantMenu,
2640
+ renderIntegration }, (renderParticipant ? { render: ({ participant }) => renderParticipant({ participant }) } : {})))), [cellViewsInPresentationGrid, renderIntegration]);
2319
2641
  const gridContent = React.useMemo(() => cellViewsVideoGrid.map((cellView) => renderCellView(Object.assign({ cellView,
2320
2642
  enableParticipantMenu }, (renderParticipant ? { render: ({ participant }) => renderParticipant({ participant }) } : {})))), [cellViewsVideoGrid]);
2321
2643
  const subgridContent = React.useMemo(() => cellViewsInSubgrid.map((cellView) => renderCellView(Object.assign({ cellView,
2322
- enableParticipantMenu }, (renderSubgridParticipant
2644
+ enableParticipantMenu,
2645
+ renderIntegration }, (renderSubgridParticipant
2323
2646
  ? { render: ({ participant }) => renderSubgridParticipant({ participant }) }
2324
- : {})))), [cellViewsInSubgrid]);
2647
+ : {})))), [cellViewsInSubgrid, renderIntegration]);
2648
+ const hiddenContent = React.useMemo(() => cellViewsHidden.map((cellView) => renderCellView({ cellView, renderIntegration })), [cellViewsHidden, renderIntegration]);
2325
2649
  React.useEffect(() => {
2326
2650
  if (!gridRef.current) {
2327
2651
  return;
@@ -2344,10 +2668,12 @@ function Grid({ renderParticipant, renderSubgridParticipant, renderFloatingParti
2344
2668
  cellViewsInPresentationGrid,
2345
2669
  cellViewsInSubgrid,
2346
2670
  clientAspectRatios,
2671
+ maximizedCellId,
2672
+ setMaximizedCellId,
2347
2673
  maximizedParticipant,
2348
- setMaximizedParticipant,
2674
+ floatingCellId,
2675
+ setFloatingCellId,
2349
2676
  floatingParticipant,
2350
- setFloatingParticipant,
2351
2677
  isConstrained,
2352
2678
  } },
2353
2679
  React.createElement("div", { ref: gridRef, style: {
@@ -2355,7 +2681,102 @@ function Grid({ renderParticipant, renderSubgridParticipant, renderFloatingParti
2355
2681
  height: "100%",
2356
2682
  position: "relative",
2357
2683
  } },
2358
- React.createElement(VideoStageLayout, { containerFrame: containerFrame, floatingContent: floatingContent, layoutVideoStage: videoStage, presentationGridContent: presentationGridContent, gridContent: gridContent, subgridContent: subgridContent }))));
2684
+ React.createElement(VideoStageLayout, { containerFrame: containerFrame, floatingContent: floatingContent, layoutVideoStage: videoStage, presentationGridContent: presentationGridContent, gridContent: gridContent, subgridContent: subgridContent, hiddenContent: hiddenContent }))));
2685
+ }
2686
+
2687
+ function subscribeToRoomIntegrationPicker({ pickerOrigin, getSource, onOutcome, }) {
2688
+ let settled = false;
2689
+ function handleMessage(event) {
2690
+ if (settled || event.origin !== pickerOrigin || !event.source || event.source !== getSource()) {
2691
+ return;
2692
+ }
2693
+ const { type, payload } = event.data || {};
2694
+ if (type === ROOM_INTEGRATION_PICKER_MESSAGES.FORM_SUBMIT) {
2695
+ settle({ type: "submitted", content: payload });
2696
+ }
2697
+ else if (type === ROOM_INTEGRATION_PICKER_MESSAGES.FORM_CLOSE) {
2698
+ settle({ type: "cancelled" });
2699
+ }
2700
+ else if (type === ROOM_INTEGRATION_PICKER_MESSAGES.ERROR) {
2701
+ settle({
2702
+ type: "error",
2703
+ error: new Error((payload === null || payload === void 0 ? void 0 : payload.message) || "The room integration picker failed"),
2704
+ });
2705
+ }
2706
+ }
2707
+ const unsubscribe = () => {
2708
+ settled = true;
2709
+ window.removeEventListener("message", handleMessage);
2710
+ };
2711
+ const settle = (outcome) => {
2712
+ unsubscribe();
2713
+ onOutcome(outcome);
2714
+ };
2715
+ window.addEventListener("message", handleMessage);
2716
+ return unsubscribe;
2717
+ }
2718
+
2719
+ const DEFAULT_ALLOW = "clipboard-read; clipboard-write; fullscreen";
2720
+ function useRoomIntegrationPicker({ integration, onPicked, onCancel, onError, featureSource, allow = DEFAULT_ALLOW, title, }) {
2721
+ const iframeRef = React.useRef(null);
2722
+ const onPickedRef = React.useRef(onPicked);
2723
+ const onCancelRef = React.useRef(onCancel);
2724
+ const onErrorRef = React.useRef(onError);
2725
+ onPickedRef.current = onPicked;
2726
+ onCancelRef.current = onCancel;
2727
+ onErrorRef.current = onError;
2728
+ const { webview } = integration;
2729
+ const pickerOrigin = React.useMemo(() => {
2730
+ try {
2731
+ return new URL(webview).origin;
2732
+ }
2733
+ catch (_a) {
2734
+ console.warn(`useRoomIntegrationPicker: unusable webview url ${webview}`);
2735
+ return null;
2736
+ }
2737
+ }, [webview]);
2738
+ const src = React.useMemo(() => {
2739
+ if (!pickerOrigin) {
2740
+ return null;
2741
+ }
2742
+ return roomIntegrationPickerUrl({
2743
+ integration: { webview },
2744
+ parentOrigin: window.location.origin,
2745
+ featureSource,
2746
+ });
2747
+ }, [pickerOrigin, webview, featureSource]);
2748
+ React.useEffect(() => {
2749
+ if (!pickerOrigin) {
2750
+ return;
2751
+ }
2752
+ return subscribeToRoomIntegrationPicker({
2753
+ pickerOrigin,
2754
+ getSource: () => { var _a; return (_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow; },
2755
+ onOutcome: (outcome) => {
2756
+ var _a, _b;
2757
+ if (outcome.type === "submitted") {
2758
+ onPickedRef.current(outcome.content || {});
2759
+ return;
2760
+ }
2761
+ if (outcome.type === "cancelled") {
2762
+ (_a = onCancelRef.current) === null || _a === void 0 ? void 0 : _a.call(onCancelRef);
2763
+ return;
2764
+ }
2765
+ (_b = onErrorRef.current) === null || _b === void 0 ? void 0 : _b.call(onErrorRef, outcome.error);
2766
+ },
2767
+ });
2768
+ }, [pickerOrigin, src]);
2769
+ useIframeContract(iframeRef, "useRoomIntegrationPicker", !!src);
2770
+ return {
2771
+ iframeProps: src
2772
+ ? {
2773
+ ref: iframeRef,
2774
+ src,
2775
+ title: title || `${integration.title} picker`,
2776
+ allow,
2777
+ }
2778
+ : null,
2779
+ };
2359
2780
  }
2360
2781
 
2361
- export { GridCell, GridVideoView, ParticipantMenu, ParticipantMenuContent, ParticipantMenuItem, ParticipantMenuTrigger, Grid as VideoGrid, VideoView, Provider as WherebyProvider, useLocalMedia, useRoomConnection };
2782
+ export { GridCell, GridVideoView, ParticipantMenu, ParticipantMenuContent, ParticipantMenuItem, ParticipantMenuTrigger, Grid as VideoGrid, VideoView, Provider as WherebyProvider, subscribeToRoomIntegrationPicker, useGrid, useLocalMedia, useRoomConnection, useRoomIntegrationPicker, useRoomIntegrationView };