@whereby.com/browser-sdk 3.31.2 → 3.32.0

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