@naviedu/room-platform-react 0.0.8 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -85,13 +85,18 @@ layout that allows the room to occupy the viewport.
85
85
  | `extensions` | No | Host-provided top-bar, workspace, sidebar, monitor, and participant extensions. |
86
86
  | `integrations` | No | Chat and whiteboard configuration. |
87
87
  | `isPracticeMode` | No | Enables practice-room presentation behavior. |
88
- | `showLocalPreview` | No | Shows the local preview in the sidebar. Defaults to `true`. |
89
- | `primaryCameraSelector` | No | Selects the primary camera track when the default selection is not suitable. |
90
88
  | `onError` | No | Receives connection, media, and action errors. |
91
89
  | `onLifecycleEvent` | No | Receives exchange, renewal, and reconnect lifecycle events. |
92
90
  | `onExit` | No | Called when the user chooses to leave the room. |
93
91
  | `children` | No | Replaces the default workspace while keeping the provider and room runtime. |
94
92
 
93
+ The default teaching stage selects the available participant camera carrying
94
+ the canonical `room:moderate` capability. Screen share remains primary and the
95
+ moderator camera becomes its overlay. If no moderator camera is available, the
96
+ stage keeps its waiting state and never promotes a student camera. The local
97
+ sidebar preview is hidden only when that selected moderator camera is local;
98
+ its existing desktop breakpoint remains unchanged.
99
+
95
100
  ### Error and lifecycle callbacks
96
101
 
97
102
  ```tsx
package/index.d.ts CHANGED
@@ -393,7 +393,6 @@ export declare type RoomPlatformExtensions = {
393
393
  topBarTrailing?: ComponentType<RoomPlatformExtensionProps>;
394
394
  monitor?: {
395
395
  roster: readonly RoomParticipantRosterEntry[];
396
- roomEligible: boolean;
397
396
  };
398
397
  mainWorkspace?: {
399
398
  placement: 'overlay' | 'alongside-stage';
@@ -617,7 +616,7 @@ export declare type RoomPlatformProviderProps = PropsWithChildren<{
617
616
  }>;
618
617
  }>;
619
618
 
620
- export declare function RoomPlatformRoom({ children, extensions, integrations, isPracticeMode, onExit, primaryCameraSelector, showLocalPreview, ...providerProps }: RoomPlatformRoomProps): JSX.Element;
619
+ export declare function RoomPlatformRoom({ children, extensions, integrations, isPracticeMode, onExit, sizing, ...providerProps }: RoomPlatformRoomProps): JSX.Element;
621
620
 
622
621
  export declare type RoomPlatformRoomProps = {
623
622
  apiBaseUrl: string;
@@ -625,8 +624,7 @@ export declare type RoomPlatformRoomProps = {
625
624
  integrations?: RoomPlatformIntegrations;
626
625
  launchToken: string;
627
626
  isPracticeMode?: boolean;
628
- primaryCameraSelector?: (cameraTracks: readonly TrackReference[], activeSpeakerParticipantIdentity?: string) => TrackReference | null;
629
- showLocalPreview?: boolean;
627
+ sizing?: RoomPlatformSizing;
630
628
  onError?: (event: RoomPlatformErrorEvent) => void;
631
629
  onLifecycleEvent?: (event: RoomPlatformLifecycleEvent) => void;
632
630
  onExit?: () => void;
@@ -640,7 +638,7 @@ export declare const RoomPlatformRuntimeErrorCode: {
640
638
  readonly LIVEKIT_TOKEN_EXPIRED: "LIVEKIT_TOKEN_EXPIRED";
641
639
  };
642
640
 
643
- export declare function RoomPlatformShell({ mainPanel, sidebar, topBar }: RoomPlatformShellProps): JSX.Element;
641
+ export declare function RoomPlatformShell({ mainPanel, sidebar, sizing, topBar }: RoomPlatformShellProps): JSX.Element;
644
642
 
645
643
  export declare type RoomPlatformShellControls = {
646
644
  isTheaterMode: boolean;
@@ -654,19 +652,21 @@ export declare type RoomPlatformShellControls = {
654
652
  };
655
653
 
656
654
  export declare type RoomPlatformShellProps = {
655
+ sizing?: RoomPlatformSizing;
657
656
  topBar: ReactNode;
658
657
  mainPanel: ReactNode | ((controls: RoomPlatformShellControls) => ReactNode);
659
658
  sidebar: ReactNode;
660
659
  };
661
660
 
662
- export declare function RoomPlatformSpeakQueue({ activeSpeakerParticipantIdentity, activeSpeakerSlot, canApproveSpeak, canClearActiveSpeaker, canRejectSpeak, isClearingActiveSpeaker, isPracticeMode, onApproveSpeakRequest, onClearActiveSpeaker, onRejectSpeakRequest, participants, raisedHandParticipantIdentities, }: {
661
+ export declare type RoomPlatformSizing = 'viewport' | 'container';
662
+
663
+ export declare function RoomPlatformSpeakQueue({ activeSpeakerParticipantIdentity, activeSpeakerSlot, canApproveSpeak, canClearActiveSpeaker, canRejectSpeak, isClearingActiveSpeaker, onApproveSpeakRequest, onClearActiveSpeaker, onRejectSpeakRequest, participants, raisedHandParticipantIdentities, }: {
663
664
  activeSpeakerParticipantIdentity?: string;
664
665
  activeSpeakerSlot?: ReactNode;
665
666
  canApproveSpeak?: boolean;
666
667
  canClearActiveSpeaker?: boolean;
667
668
  canRejectSpeak?: boolean;
668
669
  isClearingActiveSpeaker?: boolean;
669
- isPracticeMode?: boolean;
670
670
  onApproveSpeakRequest?: (participantIdentity: string) => Promise<void> | void;
671
671
  onClearActiveSpeaker?: () => void;
672
672
  onRejectSpeakRequest?: (participantIdentity: string) => Promise<void> | void;
package/index.esm.js CHANGED
@@ -173,7 +173,13 @@ var getParticipantTabTitle = function(participantTab) {
173
173
  };
174
174
 
175
175
  var RoomPlatformCapability = {
176
+ ROOM_JOIN: 'room:join',
177
+ CHAT_SEND: 'chat:send',
178
+ MEDIA_SUBSCRIBE: 'media:subscribe',
179
+ MEDIA_PUBLISH_AUDIO: 'media:publish_audio',
180
+ MEDIA_PUBLISH_VIDEO: 'media:publish_video',
176
181
  MEDIA_SHARE_SCREEN: 'media:share_screen',
182
+ DATA_PUBLISH: 'data:publish',
177
183
  ROOM_SPEAK_REQUEST: 'room:speak_request',
178
184
  ROOM_MODERATE: 'room:moderate',
179
185
  MONITOR_MANAGE: 'monitor:manage',
@@ -182,6 +188,10 @@ var RoomPlatformCapability = {
182
188
  PRIVATE_ROOM_CLOSE: 'private_room:close',
183
189
  WHITEBOARD_CLEAR: 'whiteboard:clear'
184
190
  };
191
+ var roomPlatformCapabilities = Object.values(RoomPlatformCapability);
192
+ var isRoomPlatformCapability = function(value) {
193
+ return roomPlatformCapabilities.includes(value);
194
+ };
185
195
 
186
196
  function _define_property$y(obj, key, value) {
187
197
  if (key in obj) {
@@ -9680,7 +9690,7 @@ function RoomPlatformVideoTile(param) {
9680
9690
  return /*#__PURE__*/ jsxs("div", {
9681
9691
  "aria-label": resolvedLabel,
9682
9692
  className: [
9683
- 'relative overflow-hidden rounded-lg bg-slate-900 text-white ring-1 ring-white/10',
9693
+ 'relative overflow-hidden rounded-lg bg-slate-900 text-white',
9684
9694
  className
9685
9695
  ].filter(Boolean).join(' '),
9686
9696
  "data-kind": kind,
@@ -19560,19 +19570,28 @@ function getNearestMarkerExpiry(objects) {
19560
19570
  return expiries.length ? (_Math = Math).min.apply(_Math, _to_consumable_array$5(expiries)) : undefined;
19561
19571
  }
19562
19572
  function getNormalizedPoint(event, frameRect) {
19563
- if (!Number.isFinite(frameRect.left) || !Number.isFinite(frameRect.top)) return undefined;
19564
- if (!Number.isFinite(frameRect.width) || !Number.isFinite(frameRect.height)) return undefined;
19565
- if (frameRect.width <= 0 || frameRect.height <= 0) return undefined;
19573
+ var currentFrameRect = getPointerFrameRect(event, frameRect);
19574
+ if (!currentFrameRect) return undefined;
19566
19575
  var clientX = getEventCoordinate(event, 'clientX');
19567
19576
  var clientY = getEventCoordinate(event, 'clientY');
19568
19577
  if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return undefined;
19569
- var x = (clientX - frameRect.left) / frameRect.width;
19570
- var y = (clientY - frameRect.top) / frameRect.height;
19578
+ var x = (clientX - currentFrameRect.left) / currentFrameRect.width;
19579
+ var y = (clientY - currentFrameRect.top) / currentFrameRect.height;
19571
19580
  return {
19572
19581
  x: clamp01(x),
19573
19582
  y: clamp01(y)
19574
19583
  };
19575
19584
  }
19585
+ function getPointerFrameRect(event, fallback) {
19586
+ var rect = event.currentTarget.getBoundingClientRect();
19587
+ if (Number.isFinite(rect.left) && Number.isFinite(rect.top) && Number.isFinite(rect.width) && Number.isFinite(rect.height) && rect.width > 0 && rect.height > 0) {
19588
+ return rect;
19589
+ }
19590
+ if (!Number.isFinite(fallback.left) || !Number.isFinite(fallback.top) || !Number.isFinite(fallback.width) || !Number.isFinite(fallback.height) || fallback.width <= 0 || fallback.height <= 0) {
19591
+ return undefined;
19592
+ }
19593
+ return fallback;
19594
+ }
19576
19595
  function getEventCoordinate(event, key) {
19577
19596
  var _event_nativeEvent;
19578
19597
  if (Number.isFinite(event[key])) return event[key];
@@ -26368,7 +26387,7 @@ function _unsupported_iterable_to_array$3(o, minLen) {
26368
26387
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$3(o, minLen);
26369
26388
  }
26370
26389
  function RoomPlatformShell(param) {
26371
- var mainPanel = param.mainPanel, sidebar = param.sidebar, topBar = param.topBar;
26390
+ var mainPanel = param.mainPanel, sidebar = param.sidebar, _param_sizing = param.sizing, sizing = _param_sizing === void 0 ? 'viewport' : _param_sizing, topBar = param.topBar;
26372
26391
  var _useState = _sliced_to_array$3(useState(true), 2), isSidebarOpen = _useState[0], setIsSidebarOpen = _useState[1];
26373
26392
  var _useState1 = _sliced_to_array$3(useState(false), 2), isTheaterMode = _useState1[0], setIsTheaterMode = _useState1[1];
26374
26393
  var _useState2 = _sliced_to_array$3(useState(false), 2), isFullscreen = _useState2[0], setIsFullscreen = _useState2[1];
@@ -26410,6 +26429,7 @@ function RoomPlatformShell(param) {
26410
26429
  }
26411
26430
  };
26412
26431
  var resolvedMainPanel = typeof mainPanel === 'function' ? mainPanel(controls) : mainPanel;
26432
+ var sizingClassName = sizing === 'container' ? 'h-full min-h-0 w-full' : 'h-dvh min-h-dvh w-dvw';
26413
26433
  useEffect(function() {
26414
26434
  var syncResponsiveState = function() {
26415
26435
  var isMobileViewport = typeof window.matchMedia === 'function' && window.matchMedia('(max-width: 767px)').matches;
@@ -26444,7 +26464,8 @@ function RoomPlatformShell(param) {
26444
26464
  "data-testid": "room-platform-shell",
26445
26465
  "data-theater-mode": effectiveTheaterMode,
26446
26466
  "data-mobile-fullscreen": isMobileFullscreen,
26447
- className: "flex h-dvh min-h-dvh w-dvw min-w-0 flex-col overflow-hidden bg-slate-950 text-white",
26467
+ "data-sizing": sizing,
26468
+ className: "flex ".concat(sizingClassName, " min-w-0 flex-col overflow-hidden bg-slate-950 text-white"),
26448
26469
  children: [
26449
26470
  /*#__PURE__*/ jsx("div", {
26450
26471
  className: isMobileFullscreen ? 'hidden md:block' : undefined,
@@ -26670,7 +26691,7 @@ function _ts_generator$1(thisArg, body) {
26670
26691
  }
26671
26692
  }
26672
26693
  function RoomPlatformSpeakQueue(param) {
26673
- var activeSpeakerParticipantIdentity = param.activeSpeakerParticipantIdentity, activeSpeakerSlot = param.activeSpeakerSlot, canApproveSpeak = param.canApproveSpeak, canClearActiveSpeaker = param.canClearActiveSpeaker, canRejectSpeak = param.canRejectSpeak, isClearingActiveSpeaker = param.isClearingActiveSpeaker, _param_isPracticeMode = param.isPracticeMode, isPracticeMode = _param_isPracticeMode === void 0 ? false : _param_isPracticeMode, onApproveSpeakRequest = param.onApproveSpeakRequest, onClearActiveSpeaker = param.onClearActiveSpeaker, onRejectSpeakRequest = param.onRejectSpeakRequest, participants = param.participants, raisedHandParticipantIdentities = param.raisedHandParticipantIdentities;
26694
+ var activeSpeakerParticipantIdentity = param.activeSpeakerParticipantIdentity, activeSpeakerSlot = param.activeSpeakerSlot, canApproveSpeak = param.canApproveSpeak, canClearActiveSpeaker = param.canClearActiveSpeaker, canRejectSpeak = param.canRejectSpeak, isClearingActiveSpeaker = param.isClearingActiveSpeaker, onApproveSpeakRequest = param.onApproveSpeakRequest, onClearActiveSpeaker = param.onClearActiveSpeaker, onRejectSpeakRequest = param.onRejectSpeakRequest, participants = param.participants, raisedHandParticipantIdentities = param.raisedHandParticipantIdentities;
26674
26695
  var participantsByIdentity = new Map(participants.map(function(participant) {
26675
26696
  return [
26676
26697
  participant.participantIdentity,
@@ -26749,11 +26770,11 @@ function RoomPlatformSpeakQueue(param) {
26749
26770
  "Bục giảng"
26750
26771
  ]
26751
26772
  }),
26752
- !isPracticeMode ? /*#__PURE__*/ jsx(Badge, {
26773
+ /*#__PURE__*/ jsx(Badge, {
26753
26774
  variant: "outline",
26754
26775
  className: "border-liveclass-orange/30 text-liveclass-orange",
26755
26776
  children: raisedHandParticipantIdentities.length
26756
- }) : null
26777
+ })
26757
26778
  ]
26758
26779
  }),
26759
26780
  hasActiveSpeaker ? /*#__PURE__*/ jsxs("div", {
@@ -26854,7 +26875,7 @@ function RoomPlatformSpeakQueue(param) {
26854
26875
  }, participantIdentity);
26855
26876
  }) : /*#__PURE__*/ jsx("p", {
26856
26877
  className: "text-sm text-muted-foreground",
26857
- children: isPracticeMode ? 'Danh sách chờ phát biểu sẽ được đồng bộ theo trạng thái phòng.' : 'Không có học sinh chờ phát biểu.'
26878
+ children: "Kh\xf4ng c\xf3 học sinh chờ ph\xe1t biểu."
26858
26879
  })
26859
26880
  ]
26860
26881
  });
@@ -26935,6 +26956,24 @@ function _unsupported_iterable_to_array$1(o, minLen) {
26935
26956
  if (n === "Map" || n === "Set") return Array.from(n);
26936
26957
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$1(o, minLen);
26937
26958
  }
26959
+ var cameraOverlayClassName = 'absolute left-4 top-4 z-30 aspect-video w-48 max-w-[40%]';
26960
+ function RoomPlatformCameraOverlay(param) {
26961
+ var trackRef = param.trackRef, status = param.status, testId = param.testId;
26962
+ return /*#__PURE__*/ jsx("div", {
26963
+ "data-identity": trackRef === null || trackRef === void 0 ? void 0 : trackRef.participant.identity,
26964
+ "data-status": status,
26965
+ "data-testid": testId,
26966
+ className: cameraOverlayClassName,
26967
+ style: {
26968
+ aspectRatio: getTrackAspectRatio(trackRef)
26969
+ },
26970
+ children: trackRef ? /*#__PURE__*/ jsx(RoomPlatformVideoTile, {
26971
+ kind: "overlay",
26972
+ mediaClassName: "h-full w-full overflow-hidden",
26973
+ trackRef: trackRef
26974
+ }) : 'Đang chờ tín hiệu lớp học'
26975
+ });
26976
+ }
26938
26977
  function getTrackAspectRatio(trackRef) {
26939
26978
  var _ref, _ref1;
26940
26979
  var _publication_track, _publication_track_mediaStreamTrack_getSettings, _publication_track_mediaStreamTrack, _publication_track1;
@@ -26972,17 +27011,10 @@ function RoomPlatformStage(param) {
26972
27011
  var resolvedOverlayTrack = overlayTrack !== null && overlayTrack !== void 0 ? overlayTrack : isScreenShare ? model.cameraOverlay : undefined;
26973
27012
  var frameClassName = model.primary ? 'relative flex h-full min-h-0 w-full items-center justify-center overflow-hidden rounded-lg text-white ring-1 ring-white/10' : 'relative flex h-full min-h-0 w-full items-center justify-center overflow-hidden rounded-lg bg-slate-950 p-4 text-sm text-slate-300 ring-1 ring-white/10';
26974
27013
  if (layout === 'camera-overlay') {
26975
- return /*#__PURE__*/ jsx("div", {
26976
- className: "absolute left-4 top-4 z-10 aspect-video w-48 max-w-[40%] overflow-hidden rounded-lg bg-slate-950 text-white ring-1 ring-white/20",
26977
- "data-status": status,
26978
- "data-testid": "room-platform-stage",
26979
- style: frameStyle,
26980
- children: cameraOverlayTrack ? /*#__PURE__*/ jsx(RoomPlatformVideoTile, {
26981
- className: "h-full w-full",
26982
- kind: "overlay",
26983
- mediaClassName: "h-full w-full overflow-hidden",
26984
- trackRef: cameraOverlayTrack
26985
- }) : 'Đang chờ tín hiệu lớp học'
27014
+ return /*#__PURE__*/ jsx(RoomPlatformCameraOverlay, {
27015
+ status: status,
27016
+ testId: "room-platform-stage",
27017
+ trackRef: cameraOverlayTrack
26986
27018
  });
26987
27019
  }
26988
27020
  return /*#__PURE__*/ jsxs("div", {
@@ -27019,15 +27051,9 @@ function RoomPlatformStage(param) {
27019
27051
  }) : 'Đang chờ tín hiệu lớp học'
27020
27052
  })
27021
27053
  }),
27022
- resolvedOverlayTrack ? /*#__PURE__*/ jsx("div", {
27023
- "data-identity": resolvedOverlayTrack.participant.identity,
27024
- "data-testid": "room-platform-camera-overlay",
27025
- className: "absolute left-4 top-4 z-30 aspect-video w-48 max-w-[40%] ring-1 ring-white/20",
27026
- children: /*#__PURE__*/ jsx(RoomPlatformVideoTile, {
27027
- kind: "overlay",
27028
- mediaClassName: "h-full w-full overflow-hidden",
27029
- trackRef: resolvedOverlayTrack
27030
- })
27054
+ resolvedOverlayTrack ? /*#__PURE__*/ jsx(RoomPlatformCameraOverlay, {
27055
+ testId: "room-platform-camera-overlay",
27056
+ trackRef: resolvedOverlayTrack
27031
27057
  }) : null
27032
27058
  ]
27033
27059
  });
@@ -27064,22 +27090,29 @@ function RoomPlatformTopBar(param) {
27064
27090
  });
27065
27091
  }
27066
27092
 
27067
- var isSpeakingRemote = function(param) {
27068
- var participant = param.participant;
27069
- return !participant.isLocal && participant.isSpeaking;
27070
- };
27071
- var isRemote = function(param) {
27072
- var participant = param.participant;
27073
- return !participant.isLocal;
27074
- };
27075
27093
  var hasAvailableTrackMedia = function(param) {
27076
27094
  var publication = param.publication;
27077
27095
  var state = publication;
27078
27096
  return !state.isMuted && state.isSubscribed !== false && state.track !== null;
27079
27097
  };
27098
+ var capabilitiesFromMetadata = function(metadata) {
27099
+ if (!metadata) return [];
27100
+ try {
27101
+ var capabilities = JSON.parse(metadata).capabilities;
27102
+ return Array.isArray(capabilities) && capabilities.every(function(value) {
27103
+ return typeof value === 'string' && isRoomPlatformCapability(value);
27104
+ }) ? capabilities : [];
27105
+ } catch (unused) {
27106
+ return [];
27107
+ }
27108
+ };
27109
+ var isModeratorCamera = function(param, localCapabilities) {
27110
+ var participant = param.participant;
27111
+ return (participant.isLocal ? localCapabilities : capabilitiesFromMetadata(participant.metadata)).includes(RoomPlatformCapability.ROOM_MODERATE);
27112
+ };
27080
27113
  function selectRoomPlatformStage(tracks) {
27081
- var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, activeSpeakerParticipantIdentity = _ref.activeSpeakerParticipantIdentity, primaryCameraSelector = _ref.primaryCameraSelector;
27082
- var _ref1, _cameras_find, _cameras_find1, _ref2, _shares_;
27114
+ var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, activeSpeakerParticipantIdentity = _ref.activeSpeakerParticipantIdentity, localCapabilities = _ref.localCapabilities;
27115
+ var _ref1, _shares_;
27083
27116
  var availableTracks = tracks.filter(hasAvailableTrackMedia);
27084
27117
  var shares = availableTracks.filter(function(param) {
27085
27118
  var source = param.source;
@@ -27089,13 +27122,10 @@ function selectRoomPlatformStage(tracks) {
27089
27122
  var source = param.source;
27090
27123
  return source === Track.Source.Camera;
27091
27124
  });
27092
- var genericPrimaryCamera = (_ref1 = (_cameras_find = cameras.find(isSpeakingRemote)) !== null && _cameras_find !== void 0 ? _cameras_find : cameras.find(isRemote)) !== null && _ref1 !== void 0 ? _ref1 : cameras[0];
27093
- var selectedPrimaryCamera = primaryCameraSelector ? primaryCameraSelector(cameras, activeSpeakerParticipantIdentity) : shares[0] ? (_cameras_find1 = cameras.find(function(param) {
27094
- var participant = param.participant;
27095
- var _shares_;
27096
- return participant.identity === ((_shares_ = shares[0]) === null || _shares_ === void 0 ? void 0 : _shares_.participant.identity);
27097
- })) !== null && _cameras_find1 !== void 0 ? _cameras_find1 : genericPrimaryCamera : genericPrimaryCamera;
27098
- var primary = (_ref2 = (_shares_ = shares[0]) !== null && _shares_ !== void 0 ? _shares_ : selectedPrimaryCamera) !== null && _ref2 !== void 0 ? _ref2 : undefined;
27125
+ var selectedPrimaryCamera = cameras.find(function(camera) {
27126
+ return isModeratorCamera(camera, localCapabilities !== null && localCapabilities !== void 0 ? localCapabilities : []);
27127
+ });
27128
+ var primary = (_ref1 = (_shares_ = shares[0]) !== null && _shares_ !== void 0 ? _shares_ : selectedPrimaryCamera) !== null && _ref1 !== void 0 ? _ref1 : undefined;
27099
27129
  var cameraOverlay = shares[0] ? selectedPrimaryCamera !== null && selectedPrimaryCamera !== void 0 ? selectedPrimaryCamera : undefined : undefined;
27100
27130
  var localPreview = cameras.find(function(param) {
27101
27131
  var participant = param.participant;
@@ -27123,8 +27153,9 @@ function selectRoomPlatformStage(tracks) {
27123
27153
  };
27124
27154
  }
27125
27155
 
27126
- function useRoomPlatformStage(primaryCameraSelector) {
27127
- var controlState = useRoomPlatform().controlState;
27156
+ function useRoomPlatformStage() {
27157
+ var _ref;
27158
+ var _useRoomPlatform = useRoomPlatform(), access = _useRoomPlatform.access, controlState = _useRoomPlatform.controlState;
27128
27159
  return selectRoomPlatformStage(useTracks([
27129
27160
  Track.Source.Camera,
27130
27161
  Track.Source.ScreenShare
@@ -27132,7 +27163,7 @@ function useRoomPlatformStage(primaryCameraSelector) {
27132
27163
  onlySubscribed: true
27133
27164
  }), {
27134
27165
  activeSpeakerParticipantIdentity: controlState === null || controlState === void 0 ? void 0 : controlState.activeSpeakerParticipantIdentity,
27135
- primaryCameraSelector: primaryCameraSelector
27166
+ localCapabilities: (_ref = access === null || access === void 0 ? void 0 : access.capabilities) !== null && _ref !== void 0 ? _ref : []
27136
27167
  });
27137
27168
  }
27138
27169
 
@@ -27409,6 +27440,32 @@ var emptyStageModel = {
27409
27440
  },
27410
27441
  primary: undefined
27411
27442
  };
27443
+ function RoomPlatformPracticeModeMediaPolicy(param) {
27444
+ var isPracticeMode = param.isPracticeMode;
27445
+ var _useRoomPlatform = useRoomPlatform(), localMedia = _useRoomPlatform.localMedia, setLocalMediaEnabled = _useRoomPlatform.setLocalMediaEnabled;
27446
+ var previousIsPracticeModeRef = useRef(isPracticeMode);
27447
+ var shouldStopScreenShareRef = useRef(false);
27448
+ useEffect(function() {
27449
+ if (!previousIsPracticeModeRef.current && isPracticeMode) {
27450
+ shouldStopScreenShareRef.current = true;
27451
+ }
27452
+ previousIsPracticeModeRef.current = isPracticeMode;
27453
+ if (!isPracticeMode) {
27454
+ shouldStopScreenShareRef.current = false;
27455
+ return;
27456
+ }
27457
+ if (!shouldStopScreenShareRef.current || !localMedia.screenShare) return;
27458
+ shouldStopScreenShareRef.current = false;
27459
+ void setLocalMediaEnabled('screenShare', false).catch(function() {
27460
+ return undefined;
27461
+ });
27462
+ }, [
27463
+ isPracticeMode,
27464
+ localMedia.screenShare,
27465
+ setLocalMediaEnabled
27466
+ ]);
27467
+ return null;
27468
+ }
27412
27469
  function getShareSessionKey(trackSid) {
27413
27470
  return trackSid ? "livekit:".concat(trackSid) : undefined;
27414
27471
  }
@@ -27445,13 +27502,14 @@ function RoomPlatformWhiteboardShareLifecycle(param) {
27445
27502
  return null;
27446
27503
  }
27447
27504
  var RoomPlatformWorkspaceContent = function(param) {
27448
- var cameraBackground = param.cameraBackground, integrations = param.integrations, extensions = param.extensions, isPracticeMode = param.isPracticeMode, onExit = param.onExit; param.primaryCameraSelector; var showLocalPreview = param.showLocalPreview, stageModel = param.stageModel;
27449
- var _ref, _ref1;
27450
- var _resolvedStageModel_primary, _extensions_sidebarTabs;
27505
+ var cameraBackground = param.cameraBackground, integrations = param.integrations, extensions = param.extensions; param.isPracticeMode; var onExit = param.onExit, sizing = param.sizing, stageModel = param.stageModel;
27506
+ var _resolvedStageModel_cameraOverlay, _ref, _ref1;
27507
+ var _resolvedStageModel_primary, _resolvedStageModel_primary1, _extensions_sidebarTabs;
27451
27508
  var _useRoomPlatform = useRoomPlatform(), access = _useRoomPlatform.access, controlState = _useRoomPlatform.controlState, error = _useRoomPlatform.error, hasCapability = _useRoomPlatform.hasCapability, isActionPending = _useRoomPlatform.isActionPending, localPreviewTrack = _useRoomPlatform.localPreviewTrack, performAction = _useRoomPlatform.performAction, status = _useRoomPlatform.status;
27452
27509
  var _useRoomPlatformPrivateRoom = useRoomPlatformPrivateRoom(), cancelPrivateRoom = _useRoomPlatformPrivateRoom.cancelPrivateRoom, currentPrivateRoomOperation = _useRoomPlatformPrivateRoom.currentPrivateRoomOperation;
27453
27510
  var resolvedStageModel = stageModel !== null && stageModel !== void 0 ? stageModel : emptyStageModel;
27454
- var shareSessionKey = ((_resolvedStageModel_primary = resolvedStageModel.primary) === null || _resolvedStageModel_primary === void 0 ? void 0 : _resolvedStageModel_primary.source) === Track.Source.ScreenShare ? getShareSessionKey(resolvedStageModel.primary.publication.trackSid) : undefined;
27511
+ var selectedHostCamera = (_resolvedStageModel_cameraOverlay = resolvedStageModel.cameraOverlay) !== null && _resolvedStageModel_cameraOverlay !== void 0 ? _resolvedStageModel_cameraOverlay : ((_resolvedStageModel_primary = resolvedStageModel.primary) === null || _resolvedStageModel_primary === void 0 ? void 0 : _resolvedStageModel_primary.source) === Track.Source.Camera ? resolvedStageModel.primary : undefined;
27512
+ var shareSessionKey = ((_resolvedStageModel_primary1 = resolvedStageModel.primary) === null || _resolvedStageModel_primary1 === void 0 ? void 0 : _resolvedStageModel_primary1.source) === Track.Source.ScreenShare ? getShareSessionKey(resolvedStageModel.primary.publication.trackSid) : undefined;
27455
27513
  var participants = useRoomPresence();
27456
27514
  var currentParticipantPresence = participants.find(function(participant) {
27457
27515
  return participant.isLocal;
@@ -27495,7 +27553,7 @@ var RoomPlatformWorkspaceContent = function(param) {
27495
27553
  };
27496
27554
  var participantTab = extensions === null || extensions === void 0 ? void 0 : extensions.participantTab;
27497
27555
  var monitor = extensions === null || extensions === void 0 ? void 0 : extensions.monitor;
27498
- var canUseMonitor = Boolean((monitor === null || monitor === void 0 ? void 0 : monitor.roomEligible) && hasCapability('monitor:manage'));
27556
+ var canUseMonitor = hasCapability('monitor:manage');
27499
27557
  var handleMonitorStartError = useCallback(function() {
27500
27558
  setStageView('teaching');
27501
27559
  toast.error('Không thể bật Monitor. Vui lòng thử lại.');
@@ -27600,10 +27658,10 @@ var RoomPlatformWorkspaceContent = function(param) {
27600
27658
  var activeSpeakerSlot = resolvedStageModel.activeSpeaker && !isSelfActiveSpeaker ? /*#__PURE__*/ jsx(RoomPlatformActiveSpeaker, {
27601
27659
  trackRef: resolvedStageModel.activeSpeaker
27602
27660
  }) : undefined;
27603
- var localMediaSlot = showLocalPreview ? /*#__PURE__*/ jsx(RoomPlatformLocalPreview, {
27661
+ var localMediaSlot = (selectedHostCamera === null || selectedHostCamera === void 0 ? void 0 : selectedHostCamera.participant.isLocal) ? undefined : /*#__PURE__*/ jsx(RoomPlatformLocalPreview, {
27604
27662
  localPreviewTrack: localPreviewTrack,
27605
27663
  trackRef: resolvedStageModel.localPreview
27606
- }) : undefined;
27664
+ });
27607
27665
  var activeSpeakerMediaSlot = hasActiveSpeaker && !canModerate ? activeSpeakerSlot : undefined;
27608
27666
  var isClearingActiveSpeaker = isActionPending(RoomPlatformAction.SPEAKER_CLEAR);
27609
27667
  var clearActiveSpeaker = function() {
@@ -27646,7 +27704,6 @@ var RoomPlatformWorkspaceContent = function(param) {
27646
27704
  canClearActiveSpeaker: true,
27647
27705
  canRejectSpeak: true,
27648
27706
  isClearingActiveSpeaker: isClearingActiveSpeaker,
27649
- isPracticeMode: isPracticeMode,
27650
27707
  onApproveSpeakRequest: approveSpeakRequest,
27651
27708
  onClearActiveSpeaker: clearActiveSpeaker,
27652
27709
  onRejectSpeakRequest: rejectSpeakRequest,
@@ -27705,6 +27762,7 @@ var RoomPlatformWorkspaceContent = function(param) {
27705
27762
  children: [
27706
27763
  /*#__PURE__*/ jsx("section", {
27707
27764
  className: "room-platform-room room-platform-surface",
27765
+ "data-sizing": sizing,
27708
27766
  children: /*#__PURE__*/ jsx(RoomPlatformShell, {
27709
27767
  mainPanel: function(controls) {
27710
27768
  if (stageView === 'monitor' && canUseMonitor && monitor) {
@@ -27789,6 +27847,7 @@ var RoomPlatformWorkspaceContent = function(param) {
27789
27847
  });
27790
27848
  },
27791
27849
  sidebar: sidebar,
27850
+ sizing: sizing,
27792
27851
  topBar: /*#__PURE__*/ jsx(RoomPlatformTopBar, {
27793
27852
  monitorSelector: monitorSelector,
27794
27853
  topBarLeading: topBarLeading,
@@ -27824,7 +27883,7 @@ var RoomPlatformWorkspaceContent = function(param) {
27824
27883
  });
27825
27884
  };
27826
27885
  var RoomPlatformConnectedWorkspace = function(props) {
27827
- var stageModel = useRoomPlatformStage(props.primaryCameraSelector);
27886
+ var stageModel = useRoomPlatformStage();
27828
27887
  return /*#__PURE__*/ jsx(RoomPlatformWorkspaceContent, _object_spread_props(_object_spread({}, props), {
27829
27888
  stageModel: stageModel
27830
27889
  }));
@@ -27996,29 +28055,32 @@ function RoomPlatformPrivateRoomRuntime(param) {
27996
28055
  });
27997
28056
  }
27998
28057
  function RoomPlatformRoom(_0) {
27999
- var children = _0.children, extensions = _0.extensions, integrations = _0.integrations, isPracticeMode = _0.isPracticeMode, onExit = _0.onExit, primaryCameraSelector = _0.primaryCameraSelector, _0_showLocalPreview = _0.showLocalPreview, showLocalPreview = _0_showLocalPreview === void 0 ? true : _0_showLocalPreview, providerProps = _object_without_properties(_0, [
28058
+ var children = _0.children, extensions = _0.extensions, integrations = _0.integrations, isPracticeMode = _0.isPracticeMode, onExit = _0.onExit, _0_sizing = _0.sizing, sizing = _0_sizing === void 0 ? 'viewport' : _0_sizing, providerProps = _object_without_properties(_0, [
28000
28059
  "children",
28001
28060
  "extensions",
28002
28061
  "integrations",
28003
28062
  "isPracticeMode",
28004
28063
  "onExit",
28005
- "primaryCameraSelector",
28006
- "showLocalPreview"
28064
+ "sizing"
28007
28065
  ]);
28008
- return /*#__PURE__*/ jsx(RoomPlatformProvider, _object_spread_props(_object_spread({}, providerProps), {
28009
- children: /*#__PURE__*/ jsx(RoomPlatformMediaRuntime, {
28010
- children: /*#__PURE__*/ jsx(RoomPlatformPrivateRoomRuntime, {
28011
- integrations: integrations,
28012
- children: children !== null && children !== void 0 ? children : /*#__PURE__*/ jsx(RoomPlatformWorkspace, {
28013
- extensions: extensions,
28066
+ return /*#__PURE__*/ jsxs(RoomPlatformProvider, _object_spread_props(_object_spread({}, providerProps), {
28067
+ children: [
28068
+ /*#__PURE__*/ jsx(RoomPlatformPracticeModeMediaPolicy, {
28069
+ isPracticeMode: isPracticeMode
28070
+ }),
28071
+ /*#__PURE__*/ jsx(RoomPlatformMediaRuntime, {
28072
+ children: /*#__PURE__*/ jsx(RoomPlatformPrivateRoomRuntime, {
28014
28073
  integrations: integrations,
28015
- isPracticeMode: isPracticeMode,
28016
- onExit: onExit,
28017
- primaryCameraSelector: primaryCameraSelector,
28018
- showLocalPreview: showLocalPreview
28074
+ children: children !== null && children !== void 0 ? children : /*#__PURE__*/ jsx(RoomPlatformWorkspace, {
28075
+ extensions: extensions,
28076
+ integrations: integrations,
28077
+ isPracticeMode: isPracticeMode,
28078
+ onExit: onExit,
28079
+ sizing: sizing
28080
+ })
28019
28081
  })
28020
28082
  })
28021
- })
28083
+ ]
28022
28084
  }));
28023
28085
  }
28024
28086
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naviedu/room-platform-react",
3
- "version": "0.0.8",
3
+ "version": "0.0.11",
4
4
  "type": "module",
5
5
  "main": "./index.esm.js",
6
6
  "module": "./index.esm.js",
package/styles.css CHANGED
@@ -1144,6 +1144,10 @@
1144
1144
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1145
1145
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1146
1146
  }
1147
+ .room-platform-room .ring {
1148
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1149
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1150
+ }
1147
1151
  .room-platform-room .ring-1 {
1148
1152
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1149
1153
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1170,12 +1174,6 @@
1170
1174
  --tw-ring-color: color-mix(in oklab, var(--color-white) 10%, transparent);
1171
1175
  }
1172
1176
  }
1173
- .room-platform-room .ring-white\/20 {
1174
- --tw-ring-color: color-mix(in srgb, #fff 20%, transparent);
1175
- @supports (color: color-mix(in lab, red, red)) {
1176
- --tw-ring-color: color-mix(in oklab, var(--color-white) 20%, transparent);
1177
- }
1178
- }
1179
1177
  .room-platform-room .outline-hidden {
1180
1178
  --tw-outline-style: none;
1181
1179
  outline-style: none;
@@ -3711,6 +3709,12 @@
3711
3709
  width: 100%;
3712
3710
  }
3713
3711
 
3712
+ .room-platform-room[data-sizing='container'] {
3713
+ height: 100%;
3714
+ min-height: 0;
3715
+ width: 100%;
3716
+ }
3717
+
3714
3718
  .room-platform-surface {
3715
3719
  box-sizing: border-box;
3716
3720
  }