@stream-io/video-react-sdk 2.0.0-beta.0 → 2.0.0-beta.1
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/CHANGELOG.md +24 -0
- package/dist/css/embedded.css +2592 -1498
- package/dist/css/embedded.css.map +1 -1
- package/dist/css/styles.css +2388 -1222
- package/dist/css/styles.css.map +1 -1
- package/dist/embedded.cjs.js +535 -476
- package/dist/embedded.cjs.js.map +1 -1
- package/dist/esm/index.js +10 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/components/Avatar/Avatar.js +13 -5
- package/dist/esm/src/components/Avatar/Avatar.js.map +1 -1
- package/dist/esm/src/components/Badge/Badge.js +21 -0
- package/dist/esm/src/components/Badge/Badge.js.map +1 -0
- package/dist/esm/src/components/Button/Button.js +10 -0
- package/dist/esm/src/components/Button/Button.js.map +1 -0
- package/dist/esm/src/components/Button/CompositeButton.js +10 -17
- package/dist/esm/src/components/Button/CompositeButton.js.map +1 -1
- package/dist/esm/src/components/Button/IconButton.js +3 -9
- package/dist/esm/src/components/Button/IconButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/AcceptCallButton.js +3 -2
- package/dist/esm/src/components/CallControls/AcceptCallButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/CallControls.js +3 -0
- package/dist/esm/src/components/CallControls/CallControls.js.map +1 -1
- package/dist/esm/src/components/CallControls/CancelCallButton.js +11 -6
- package/dist/esm/src/components/CallControls/CancelCallButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/ReactionsButton.js +7 -3
- package/dist/esm/src/components/CallControls/ReactionsButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/RecordCallButton.js +17 -11
- package/dist/esm/src/components/CallControls/RecordCallButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/ScreenShareButton.js +11 -4
- package/dist/esm/src/components/CallControls/ScreenShareButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/ToggleAudioButton.js +28 -17
- package/dist/esm/src/components/CallControls/ToggleAudioButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js +9 -4
- package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js.map +1 -1
- package/dist/esm/src/components/CallControls/ToggleVideoButton.js +32 -23
- package/dist/esm/src/components/CallControls/ToggleVideoButton.js.map +1 -1
- package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js +3 -3
- package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js.map +1 -1
- package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js +8 -3
- package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js.map +1 -1
- package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js +20 -7
- package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -1
- package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js +8 -5
- package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js.map +1 -1
- package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js +5 -2
- package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js.map +1 -1
- package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js +7 -3
- package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js.map +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js.map +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceSelector.js +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceSelector.js.map +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js +2 -2
- package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js.map +1 -1
- package/dist/esm/src/components/DeviceSettings/DeviceSettings.js +8 -5
- package/dist/esm/src/components/DeviceSettings/DeviceSettings.js.map +1 -1
- package/dist/esm/src/components/DeviceSettings/SpeakerTest.js +10 -4
- package/dist/esm/src/components/DeviceSettings/SpeakerTest.js.map +1 -1
- package/dist/esm/src/components/DropdownSelect/DropdownSelect.js +1 -1
- package/dist/esm/src/components/DropdownSelect/DropdownSelect.js.map +1 -1
- package/dist/esm/src/components/Menu/GenericMenu.js +2 -1
- package/dist/esm/src/components/Menu/GenericMenu.js.map +1 -1
- package/dist/esm/src/components/Menu/MenuToggle.js +3 -0
- package/dist/esm/src/components/Menu/MenuToggle.js.map +1 -1
- package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js +6 -3
- package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js.map +1 -1
- package/dist/esm/src/components/Notification/Notification.js +12 -3
- package/dist/esm/src/components/Notification/Notification.js.map +1 -1
- package/dist/esm/src/components/Notification/PermissionNotification.js +2 -2
- package/dist/esm/src/components/Notification/PermissionNotification.js.map +1 -1
- package/dist/esm/src/components/Notification/RecordingInProgressNotification.js +7 -3
- package/dist/esm/src/components/Notification/RecordingInProgressNotification.js.map +1 -1
- package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js +7 -2
- package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js.map +1 -1
- package/dist/esm/src/components/Permissions/PermissionRequests.js +13 -12
- package/dist/esm/src/components/Permissions/PermissionRequests.js.map +1 -1
- package/dist/esm/src/components/RingingCall/RingingCall.js +38 -14
- package/dist/esm/src/components/RingingCall/RingingCall.js.map +1 -1
- package/dist/esm/src/components/RingingCall/RingingCallControls.js +7 -3
- package/dist/esm/src/components/RingingCall/RingingCallControls.js.map +1 -1
- package/dist/esm/src/components/Search/SearchInput.js +1 -1
- package/dist/esm/src/components/Search/SearchInput.js.map +1 -1
- package/dist/esm/src/components/StreamTheme/StreamTheme.js +2 -2
- package/dist/esm/src/components/StreamTheme/StreamTheme.js.map +1 -1
- package/dist/esm/src/components/Tooltip/Tooltip.js +8 -8
- package/dist/esm/src/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/esm/src/components/VideoPreview/VideoPreview.js +36 -5
- package/dist/esm/src/components/VideoPreview/VideoPreview.js.map +1 -1
- package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js +38 -12
- package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js.map +1 -1
- package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js +9 -5
- package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -1
- package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js +9 -6
- package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js.map +1 -1
- package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js +9 -6
- package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js.map +1 -1
- package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js +11 -6
- package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js.map +1 -1
- package/dist/esm/src/core/components/ParticipantView/ConnectionQualityIndicator.js +25 -0
- package/dist/esm/src/core/components/ParticipantView/ConnectionQualityIndicator.js.map +1 -0
- package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js +17 -13
- package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js.map +1 -1
- package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js +13 -8
- package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js.map +1 -1
- package/dist/esm/src/core/components/StreamVideo/StreamVideo.js +24 -3
- package/dist/esm/src/core/components/StreamVideo/StreamVideo.js.map +1 -1
- package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js +3 -13
- package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js.map +1 -1
- package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js +4 -2
- package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js.map +1 -1
- package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js +4 -2
- package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js.map +1 -1
- package/dist/esm/src/embedded/EmbeddedClientProvider.js +7 -4
- package/dist/esm/src/embedded/EmbeddedClientProvider.js.map +1 -1
- package/dist/esm/src/embedded/call/CallControls.js +10 -6
- package/dist/esm/src/embedded/call/CallControls.js.map +1 -1
- package/dist/esm/src/embedded/call/CallHeader.js +10 -7
- package/dist/esm/src/embedded/call/CallHeader.js.map +1 -1
- package/dist/esm/src/embedded/call/CallLayout.js +8 -5
- package/dist/esm/src/embedded/call/CallLayout.js.map +1 -1
- package/dist/esm/src/embedded/call/CallStateRouter.js +7 -4
- package/dist/esm/src/embedded/call/CallStateRouter.js.map +1 -1
- package/dist/esm/src/embedded/hooks/useLayout.js +7 -4
- package/dist/esm/src/embedded/hooks/useLayout.js.map +1 -1
- package/dist/esm/src/embedded/livestream/host/HostLayout.js +12 -7
- package/dist/esm/src/embedded/livestream/host/HostLayout.js.map +1 -1
- package/dist/esm/src/embedded/livestream/host/HostStateRouter.js +12 -7
- package/dist/esm/src/embedded/livestream/host/HostStateRouter.js.map +1 -1
- package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js +9 -6
- package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js.map +1 -1
- package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js +12 -9
- package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js.map +1 -1
- package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js +7 -4
- package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js.map +1 -1
- package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js +13 -9
- package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js.map +1 -1
- package/dist/esm/src/embedded/shared/CallDuration/CallDuration.js +40 -0
- package/dist/esm/src/embedded/shared/CallDuration/CallDuration.js.map +1 -0
- package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js +8 -6
- package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js.map +1 -1
- package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js +26 -2
- package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js.map +1 -1
- package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js +12 -6
- package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js.map +1 -1
- package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js +8 -6
- package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js.map +1 -1
- package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js +15 -14
- package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js.map +1 -1
- package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js +4 -0
- package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js.map +1 -1
- package/dist/esm/src/embedded/shared/JoinError/JoinError.js +8 -6
- package/dist/esm/src/embedded/shared/JoinError/JoinError.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js +11 -7
- package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js +6 -4
- package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/Lobby.js +10 -9
- package/dist/esm/src/embedded/shared/Lobby/Lobby.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js +6 -4
- package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js +8 -6
- package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js.map +1 -1
- package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js +11 -9
- package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js.map +1 -1
- package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js +6 -4
- package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js.map +1 -1
- package/dist/esm/src/hooks/useDeviceList.js +4 -2
- package/dist/esm/src/hooks/useDeviceList.js.map +1 -1
- package/dist/esm/src/i18n/Streami18n.js +58 -0
- package/dist/esm/src/i18n/Streami18n.js.map +1 -0
- package/dist/esm/src/i18n/TranslationContext.js +42 -0
- package/dist/esm/src/i18n/TranslationContext.js.map +1 -0
- package/dist/esm/src/i18n/runtimeDefaults.js +20 -0
- package/dist/esm/src/i18n/runtimeDefaults.js.map +1 -0
- package/dist/esm/src/i18n/useStreami18n.js +49 -0
- package/dist/esm/src/i18n/useStreami18n.js.map +1 -0
- package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js +4 -0
- package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js.map +1 -1
- package/dist/index.cjs.js +559 -623
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/src/components/Avatar/Avatar.d.ts +9 -4
- package/dist/src/components/Badge/Badge.d.ts +23 -0
- package/dist/src/components/Badge/index.d.ts +1 -0
- package/dist/src/components/Button/Button.d.ts +16 -0
- package/dist/src/components/Button/CompositeButton.d.ts +6 -3
- package/dist/src/components/Button/IconButton.d.ts +7 -7
- package/dist/src/components/Button/index.d.ts +2 -2
- package/dist/src/components/CallControls/CancelCallButton.d.ts +3 -1
- package/dist/src/components/CallControls/ToggleAudioButton.d.ts +3 -3
- package/dist/src/components/CallControls/ToggleAudioOutputButton.d.ts +2 -2
- package/dist/src/components/CallControls/ToggleVideoButton.d.ts +3 -3
- package/dist/src/components/CallControls/index.d.ts +0 -1
- package/dist/src/components/Menu/GenericMenu.d.ts +1 -0
- package/dist/src/components/Notification/Notification.d.ts +2 -1
- package/dist/src/components/StreamTheme/StreamTheme.d.ts +2 -1
- package/dist/src/components/VideoPreview/VideoPreview.d.ts +6 -1
- package/dist/src/components/index.d.ts +1 -1
- package/dist/src/core/components/ParticipantView/ConnectionQualityIndicator.d.ts +10 -0
- package/dist/src/core/components/ParticipantView/index.d.ts +1 -0
- package/dist/src/core/components/StreamVideo/StreamVideo.d.ts +18 -2
- package/dist/src/embedded/shared/CallDuration/CallDuration.d.ts +9 -0
- package/dist/src/embedded/shared/index.d.ts +1 -0
- package/dist/src/i18n/Streami18n.d.ts +56 -0
- package/dist/src/i18n/TranslationContext.d.ts +30 -0
- package/dist/src/i18n/index.d.ts +16 -0
- package/dist/src/i18n/keys.d.ts +164 -0
- package/dist/src/i18n/runtimeDefaults.d.ts +17 -0
- package/dist/src/i18n/types.d.ts +45 -0
- package/dist/src/i18n/useStreami18n.d.ts +19 -0
- package/index.ts +1 -1
- package/package.json +18 -13
- package/src/components/Avatar/Avatar.tsx +53 -19
- package/src/components/Badge/Badge.tsx +48 -0
- package/src/components/Badge/index.ts +1 -0
- package/src/components/Button/Button.tsx +49 -0
- package/src/components/Button/CompositeButton.tsx +82 -73
- package/src/components/Button/IconButton.tsx +19 -29
- package/src/components/Button/index.ts +2 -2
- package/src/components/CallControls/AcceptCallButton.tsx +3 -1
- package/src/components/CallControls/CancelCallButton.tsx +32 -24
- package/src/components/CallControls/ReactionsButton.tsx +7 -3
- package/src/components/CallControls/RecordCallButton.tsx +50 -23
- package/src/components/CallControls/ScreenShareButton.tsx +27 -6
- package/src/components/CallControls/ToggleAudioButton.tsx +62 -61
- package/src/components/CallControls/ToggleAudioOutputButton.tsx +7 -4
- package/src/components/CallControls/ToggleVideoButton.tsx +66 -66
- package/src/components/CallControls/index.ts +0 -1
- package/src/components/CallParticipantsList/BlockedUserListing.tsx +9 -2
- package/src/components/CallParticipantsList/CallParticipantListHeader.tsx +10 -3
- package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +33 -8
- package/src/components/CallParticipantsList/CallParticipantsList.tsx +21 -5
- package/src/components/CallParticipantsList/EmptyParticipantSearchList.tsx +2 -2
- package/src/components/CallRecordingList/CallRecordingListHeader.tsx +11 -3
- package/src/components/DeviceSettings/DeviceAudioPreviewItem.tsx +3 -1
- package/src/components/DeviceSettings/DeviceSelector.tsx +1 -1
- package/src/components/DeviceSettings/DeviceSelectorAudio.tsx +2 -7
- package/src/components/DeviceSettings/DeviceSettings.tsx +14 -5
- package/src/components/DeviceSettings/SpeakerTest.tsx +7 -2
- package/src/components/DropdownSelect/DropdownSelect.tsx +3 -1
- package/src/components/Menu/GenericMenu.tsx +4 -0
- package/src/components/Notification/MicCaptureErrorNotification.tsx +7 -2
- package/src/components/Notification/Notification.tsx +20 -4
- package/src/components/Notification/PermissionNotification.tsx +2 -0
- package/src/components/Notification/RecordingInProgressNotification.tsx +5 -6
- package/src/components/Notification/SpeakingWhileMutedNotification.tsx +8 -2
- package/src/components/Permissions/PermissionRequests.tsx +50 -43
- package/src/components/RingingCall/RingingCall.tsx +40 -14
- package/src/components/RingingCall/RingingCallControls.tsx +2 -1
- package/src/components/Search/SearchInput.tsx +2 -3
- package/src/components/StreamTheme/StreamTheme.tsx +3 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -10
- package/src/components/VideoPreview/VideoPreview.tsx +50 -5
- package/src/components/index.ts +1 -1
- package/src/core/components/CallLayout/LivestreamLayout.tsx +48 -18
- package/src/core/components/CallLayout/PaginatedGridLayout.tsx +14 -0
- package/src/core/components/CallLayout/PipLayout/Grid.tsx +10 -2
- package/src/core/components/CallLayout/PipLayout/Pip.tsx +6 -2
- package/src/core/components/CallLayout/SpeakerLayout.tsx +27 -0
- package/src/core/components/CallLayout/partcipantFilter.test.ts +38 -31
- package/src/core/components/ParticipantView/ConnectionQualityIndicator.tsx +54 -0
- package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +60 -27
- package/src/core/components/ParticipantView/ParticipantActionsContextMenu.tsx +63 -22
- package/src/core/components/ParticipantView/index.ts +1 -0
- package/src/core/components/StreamVideo/StreamVideo.tsx +39 -4
- package/src/core/components/Video/BaseVideoPlaceholder.tsx +13 -30
- package/src/core/components/Video/DefaultPictureInPicturePlaceholder.tsx +5 -2
- package/src/core/components/Video/DefaultVideoPlaceholder.tsx +2 -2
- package/src/embedded/call/CallControls.tsx +9 -18
- package/src/embedded/call/CallHeader.tsx +3 -12
- package/src/embedded/call/CallLayout.tsx +1 -1
- package/src/embedded/livestream/host/HostLayout.tsx +26 -20
- package/src/embedded/livestream/host/HostStateRouter.tsx +12 -8
- package/src/embedded/livestream/viewer/ViewerLayout.tsx +8 -11
- package/src/embedded/livestream/viewer/ViewerLobby.tsx +31 -15
- package/src/embedded/shared/BlurToggleButton/BlurToggleButton.tsx +18 -12
- package/src/embedded/shared/CallDuration/CallDuration.tsx +32 -0
- package/src/embedded/shared/CallFeedback/CallEndedScreen.tsx +27 -13
- package/src/embedded/shared/CallFeedback/RatingScreen.tsx +20 -12
- package/src/embedded/shared/CallFeedback/StarRating.tsx +10 -3
- package/src/embedded/shared/CallFeedback/ThankYouScreen.tsx +6 -3
- package/src/embedded/shared/ConnectionNotification/ConnectionNotification.tsx +21 -14
- package/src/embedded/shared/JoinError/JoinError.tsx +7 -6
- package/src/embedded/shared/Lobby/DeviceControls.tsx +25 -3
- package/src/embedded/shared/Lobby/Lobby.tsx +19 -22
- package/src/embedded/shared/Lobby/ToggleMicButton.tsx +10 -3
- package/src/embedded/shared/Lobby/VideoPreviewFallbacks.tsx +4 -2
- package/src/embedded/shared/index.ts +1 -0
- package/src/hooks/useDeviceList.tsx +2 -2
- package/src/i18n/Streami18n.ts +67 -0
- package/src/i18n/TranslationContext.tsx +55 -0
- package/src/i18n/index.ts +22 -0
- package/src/i18n/keys.ts +169 -0
- package/src/i18n/runtimeDefaults.ts +17 -0
- package/src/i18n/types.ts +78 -0
- package/src/i18n/useStreami18n.ts +66 -0
- package/src/utilities/filter.test.ts +132 -124
- package/src/utilities/normalizeString.test.ts +50 -48
- package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js +0 -59
- package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js.map +0 -1
- package/dist/esm/src/components/Button/TextButton.js +0 -8
- package/dist/esm/src/components/Button/TextButton.js.map +0 -1
- package/dist/esm/src/components/CallControls/CallStatsButton.js +0 -18
- package/dist/esm/src/components/CallControls/CallStatsButton.js.map +0 -1
- package/dist/esm/src/components/CallStats/CallStats.js +0 -180
- package/dist/esm/src/components/CallStats/CallStats.js.map +0 -1
- package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js +0 -57
- package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js.map +0 -1
- package/dist/esm/src/translations/en.json.js +0 -177
- package/dist/esm/src/translations/en.json.js.map +0 -1
- package/dist/esm/src/translations/index.js +0 -6
- package/dist/esm/src/translations/index.js.map +0 -1
- package/dist/latency-chart-CpL1M_s0.cjs.js +0 -59
- package/dist/latency-chart-CpL1M_s0.cjs.js.map +0 -1
- package/dist/src/components/Button/TextButton.d.ts +0 -2
- package/dist/src/components/CallControls/CallStatsButton.d.ts +0 -4
- package/dist/src/components/CallStats/CallStats.d.ts +0 -22
- package/dist/src/components/CallStats/CallStatsLatencyChart.d.ts +0 -7
- package/dist/src/components/CallStats/index.d.ts +0 -1
- package/dist/src/translations/index.d.ts +0 -146
- package/src/components/Button/TextButton.tsx +0 -12
- package/src/components/CallControls/CallStatsButton.tsx +0 -37
- package/src/components/CallStats/CallStats.tsx +0 -439
- package/src/components/CallStats/CallStatsLatencyChart.tsx +0 -76
- package/src/components/CallStats/index.ts +0 -1
- package/src/translations/en.json +0 -154
- package/src/translations/index.ts +0 -3
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { useCall } from '@stream-io/video-react-bindings';
|
|
3
|
+
import '@stream-io/i18n';
|
|
4
|
+
import { useI18n } from '../../../../i18n/TranslationContext.js';
|
|
3
5
|
import { useState, useEffect, useMemo } from 'react';
|
|
6
|
+
import '@stream-io/i18n/react';
|
|
4
7
|
import clsx from 'clsx';
|
|
5
8
|
import { hasScreenShare } from '@stream-io/video-client';
|
|
6
9
|
import '../../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
|
|
7
|
-
import '../../../../components/
|
|
10
|
+
import '../../../../components/Badge/Badge.js';
|
|
11
|
+
import '../../../../components/Button/Button.js';
|
|
8
12
|
import { IconButton } from '../../../../components/Button/IconButton.js';
|
|
13
|
+
import '../../../../components/Button/CompositeButton.js';
|
|
9
14
|
import '@floating-ui/react';
|
|
10
15
|
import '../../../../components/CallControls/RecordCallButton.js';
|
|
11
16
|
import '../../../../components/CallControls/ReactionsButton.js';
|
|
@@ -14,12 +19,9 @@ import '../../../../components/DeviceSettings/DeviceSettings.js';
|
|
|
14
19
|
import '../../../../components/DropdownSelect/DropdownSelect.js';
|
|
15
20
|
import '../../../../components/Menu/MenuToggle.js';
|
|
16
21
|
import '../../../../components/CallControls/CancelCallButton.js';
|
|
17
|
-
import '../../../../components/CallControls/CallStatsButton.js';
|
|
18
22
|
import '../../../../components/CallParticipantsList/CallParticipantsList.js';
|
|
19
23
|
import '../../../../components/CallParticipantsList/CallParticipantListingItem.js';
|
|
20
|
-
import '../../../../components/CallStats/CallStats.js';
|
|
21
24
|
import '../../../../components/NoiseCancellation/NoiseCancellationProvider.js';
|
|
22
|
-
import '../../../../components/RingingCall/RingingCall.js';
|
|
23
25
|
import '../../../../components/Permissions/PermissionRequests.js';
|
|
24
26
|
import '../../Video/Video.js';
|
|
25
27
|
import '../../Video/BaseVideo.js';
|
|
@@ -27,6 +29,7 @@ import '../../Video/DefaultVideoPlaceholder.js';
|
|
|
27
29
|
import '../../ParticipantView/ParticipantViewContext.js';
|
|
28
30
|
import { ParticipantView } from '../../ParticipantView/ParticipantView.js';
|
|
29
31
|
import { DefaultParticipantViewUI } from '../../ParticipantView/DefaultParticipantViewUI.js';
|
|
32
|
+
import '../../ParticipantView/ConnectionQualityIndicator.js';
|
|
30
33
|
import { useFilteredParticipants, usePaginatedLayoutSortPreset } from '../hooks.js';
|
|
31
34
|
import { chunk } from '../../../../utilities/chunk.js';
|
|
32
35
|
|
|
@@ -72,7 +75,7 @@ const Grid = (props) => {
|
|
|
72
75
|
if (!call)
|
|
73
76
|
return null;
|
|
74
77
|
return (jsxs("div", { className: "str-video__pip-layout str-video__pip-layout--grid", ref: setWrapperElement, children: [screenSharingParticipant &&
|
|
75
|
-
(screenSharingParticipant.isLocalParticipant ? (jsxs("div", { className: "str-video__pip-screen-share-local", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__pip-screen-share-local__title", children: t('You are presenting your screen') })] })) : (jsx(ParticipantView, { participant: screenSharingParticipant, trackType: "screenShareTrack", muteAudio: true, mirror: false, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }))), jsxs("div", { className: "str-video__pip-layout__grid-container", children: [pageArrowsVisible && page > 0 && (jsx(IconButton, { icon: "caret-left", onClick: () => setPage((currentPage) => Math.max(0, currentPage - 1)), className: "str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--left" })), selectedGroup && (jsx("div", { className: clsx('str-video__pip-layout__grid', `str-video__pip-layout__grid--${getGridDensity(selectedGroup.length)}`), children: selectedGroup.map((participant) => (jsx(ParticipantView, { participant: participant, muteAudio: true, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }, participant.sessionId))) })), pageArrowsVisible && page < pageCount - 1 && (jsx(IconButton, { icon: "caret-right", onClick: () => setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1)), className: "str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--right" }))] })] }));
|
|
78
|
+
(screenSharingParticipant.isLocalParticipant ? (jsxs("div", { className: "str-video__pip-screen-share-local", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__pip-screen-share-local__title", children: t('common.presentingYourScreen.text', 'You are presenting your screen') })] })) : (jsx(ParticipantView, { participant: screenSharingParticipant, trackType: "screenShareTrack", muteAudio: true, mirror: false, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }))), jsxs("div", { className: "str-video__pip-layout__grid-container", children: [pageArrowsVisible && page > 0 && (jsx(IconButton, { icon: "caret-left", size: "sm", variant: "secondary", onClick: () => setPage((currentPage) => Math.max(0, currentPage - 1)), className: "str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--left" })), selectedGroup && (jsx("div", { className: clsx('str-video__pip-layout__grid', `str-video__pip-layout__grid--${getGridDensity(selectedGroup.length)}`), children: selectedGroup.map((participant) => (jsx(ParticipantView, { participant: participant, muteAudio: true, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }, participant.sessionId))) })), pageArrowsVisible && page < pageCount - 1 && (jsx(IconButton, { icon: "caret-right", size: "sm", variant: "secondary", onClick: () => setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1)), className: "str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--right" }))] })] }));
|
|
76
79
|
};
|
|
77
80
|
Grid.displayName = 'PipLayout.Grid';
|
|
78
81
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.js","sources":["../../../../../../../../../src/core/components/CallLayout/PipLayout/Grid.tsx"],"sourcesContent":["import { useCall
|
|
1
|
+
{"version":3,"file":"Grid.js","sources":["../../../../../../../../../src/core/components/CallLayout/PipLayout/Grid.tsx"],"sourcesContent":["import { useCall } from '@stream-io/video-react-bindings';\nimport { useI18n } from '../../../../i18n';\nimport { useEffect, useMemo, useState } from 'react';\nimport clsx from 'clsx';\nimport { hasScreenShare } from '@stream-io/video-client';\nimport { Icon, IconButton } from '../../../../components';\nimport {\n DefaultParticipantViewUI,\n ParticipantView,\n} from '../../ParticipantView';\nimport {\n useFilteredParticipants,\n usePaginatedLayoutSortPreset,\n} from '../hooks';\nimport { chunk } from '../../../../utilities';\nimport { PipLayoutProps } from './Pip';\n\ntype GridDensity = 'single' | 'small' | 'medium' | 'large' | 'overflow';\n\nexport type PipLayoutGridProps = PipLayoutProps & {\n /**\n * The number of participants to display per page.\n * @default 9\n */\n groupSize?: number;\n\n /**\n * Whether to show pagination arrows when there are multiple pages.\n * @default true\n */\n pageArrowsVisible?: boolean;\n};\n\nconst getGridDensity = (count: number): GridDensity => {\n if (count === 1) return 'single';\n if (count <= 5) return 'small';\n if (count <= 9) return 'medium';\n if (count <= 16) return 'large';\n return 'overflow';\n};\n\n/**\n * A grid-based PIP layout with pagination support.\n * Use this when you need a more structured grid view in PIP mode.\n */\nexport const Grid = (props: PipLayoutGridProps) => {\n const { t } = useI18n();\n const {\n excludeLocalParticipant = false,\n filterParticipants,\n mirrorLocalParticipantVideo = true,\n groupSize = 9,\n pageArrowsVisible = true,\n VideoPlaceholder,\n ParticipantViewUI = DefaultParticipantViewUI,\n } = props;\n\n const [page, setPage] = useState(0);\n const [wrapperElement, setWrapperElement] = useState<HTMLDivElement | null>(\n null,\n );\n\n const call = useCall();\n const participants = useFilteredParticipants({\n excludeLocalParticipant,\n filterParticipants,\n });\n const screenSharingParticipant = participants.find((p) => hasScreenShare(p));\n\n usePaginatedLayoutSortPreset(call);\n\n useEffect(() => {\n if (!wrapperElement || !call) return;\n return call.setViewport(wrapperElement);\n }, [wrapperElement, call]);\n\n const participantGroups = useMemo(\n () => chunk(participants, groupSize),\n [participants, groupSize],\n );\n\n const pageCount = participantGroups.length;\n\n if (page > pageCount - 1) {\n setPage(Math.max(0, pageCount - 1));\n }\n\n const selectedGroup = participantGroups[page];\n const mirror = mirrorLocalParticipantVideo ? undefined : false;\n\n if (!call) return null;\n\n return (\n <div\n className=\"str-video__pip-layout str-video__pip-layout--grid\"\n ref={setWrapperElement}\n >\n {screenSharingParticipant &&\n (screenSharingParticipant.isLocalParticipant ? (\n <div className=\"str-video__pip-screen-share-local\">\n <Icon icon=\"screen-share-off\" />\n <span className=\"str-video__pip-screen-share-local__title\">\n {t(\n 'common.presentingYourScreen.text',\n 'You are presenting your screen',\n )}\n </span>\n </div>\n ) : (\n <ParticipantView\n participant={screenSharingParticipant}\n trackType=\"screenShareTrack\"\n muteAudio\n mirror={false}\n VideoPlaceholder={VideoPlaceholder}\n ParticipantViewUI={ParticipantViewUI}\n />\n ))}\n <div className=\"str-video__pip-layout__grid-container\">\n {pageArrowsVisible && page > 0 && (\n <IconButton\n icon=\"caret-left\"\n size=\"sm\"\n variant=\"secondary\"\n onClick={() =>\n setPage((currentPage) => Math.max(0, currentPage - 1))\n }\n className=\"str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--left\"\n />\n )}\n {selectedGroup && (\n <div\n className={clsx(\n 'str-video__pip-layout__grid',\n `str-video__pip-layout__grid--${getGridDensity(selectedGroup.length)}`,\n )}\n >\n {selectedGroup.map((participant) => (\n <ParticipantView\n key={participant.sessionId}\n participant={participant}\n muteAudio\n mirror={mirror}\n VideoPlaceholder={VideoPlaceholder}\n ParticipantViewUI={ParticipantViewUI}\n />\n ))}\n </div>\n )}\n {pageArrowsVisible && page < pageCount - 1 && (\n <IconButton\n icon=\"caret-right\"\n size=\"sm\"\n variant=\"secondary\"\n onClick={() =>\n setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1))\n }\n className=\"str-video__pip-layout__pagination-button str-video__pip-layout__pagination-button--right\"\n />\n )}\n </div>\n </div>\n );\n};\n\nGrid.displayName = 'PipLayout.Grid';\n"],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAM,cAAc,GAAG,CAAC,KAAa,KAAiB;IACpD,IAAI,KAAK,KAAK,CAAC;AAAE,QAAA,OAAO,QAAQ;IAChC,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,OAAO;IAC9B,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,QAAQ;IAC/B,IAAI,KAAK,IAAI,EAAE;AAAE,QAAA,OAAO,OAAO;AAC/B,IAAA,OAAO,UAAU;AACnB,CAAC;AAED;;;AAGG;AACI,MAAM,IAAI,GAAG,CAAC,KAAyB,KAAI;AAChD,IAAA,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE;IACvB,MAAM,EACJ,uBAAuB,GAAG,KAAK,EAC/B,kBAAkB,EAClB,2BAA2B,GAAG,IAAI,EAClC,SAAS,GAAG,CAAC,EACb,iBAAiB,GAAG,IAAI,EACxB,gBAAgB,EAChB,iBAAiB,GAAG,wBAAwB,GAC7C,GAAG,KAAK;IAET,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IACnC,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAClD,IAAI,CACL;AAED,IAAA,MAAM,IAAI,GAAG,OAAO,EAAE;IACtB,MAAM,YAAY,GAAG,uBAAuB,CAAC;QAC3C,uBAAuB;QACvB,kBAAkB;AACnB,KAAA,CAAC;AACF,IAAA,MAAM,wBAAwB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC;IAE5E,4BAA4B,CAAC,IAAI,CAAC;IAElC,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI;YAAE;AAC9B,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;AACzC,IAAA,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAE1B,MAAM,iBAAiB,GAAG,OAAO,CAC/B,MAAM,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC,EACpC,CAAC,YAAY,EAAE,SAAS,CAAC,CAC1B;AAED,IAAA,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM;AAE1C,IAAA,IAAI,IAAI,GAAG,SAAS,GAAG,CAAC,EAAE;AACxB,QAAA,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;IACrC;AAEA,IAAA,MAAM,aAAa,GAAG,iBAAiB,CAAC,IAAI,CAAC;IAC7C,MAAM,MAAM,GAAG,2BAA2B,GAAG,SAAS,GAAG,KAAK;AAE9D,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,IAAI;IAEtB,QACEA,IAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,mDAAmD,EAC7D,GAAG,EAAE,iBAAiB,EAAA,QAAA,EAAA,CAErB,wBAAwB;iBACtB,wBAAwB,CAAC,kBAAkB,IAC1CA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mCAAmC,EAAA,QAAA,EAAA,CAChDC,GAAA,CAAC,IAAI,EAAA,EAAC,IAAI,EAAC,kBAAkB,EAAA,CAAG,EAChCA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,0CAA0C,EAAA,QAAA,EACvD,CAAC,CACA,kCAAkC,EAClC,gCAAgC,CACjC,EAAA,CACI,CAAA,EAAA,CACH,KAENA,GAAA,CAAC,eAAe,EAAA,EACd,WAAW,EAAE,wBAAwB,EACrC,SAAS,EAAC,kBAAkB,EAC5B,SAAS,EAAA,IAAA,EACT,MAAM,EAAE,KAAK,EACb,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,iBAAiB,EAAA,CACpC,CACH,CAAC,EACJD,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,uCAAuC,EAAA,QAAA,EAAA,CACnD,iBAAiB,IAAI,IAAI,GAAG,CAAC,KAC5BC,GAAA,CAAC,UAAU,EAAA,EACT,IAAI,EAAC,YAAY,EACjB,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,WAAW,EACnB,OAAO,EAAE,MACP,OAAO,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,EAExD,SAAS,EAAC,yFAAyF,GACnG,CACH,EACA,aAAa,KACZA,GAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,IAAI,CACb,6BAA6B,EAC7B,CAAA,6BAAA,EAAgC,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA,CAAE,CACvE,EAAA,QAAA,EAEA,aAAa,CAAC,GAAG,CAAC,CAAC,WAAW,MAC7BA,GAAA,CAAC,eAAe,EAAA,EAEd,WAAW,EAAE,WAAW,EACxB,SAAS,EAAA,IAAA,EACT,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,iBAAiB,EAAA,EAL/B,WAAW,CAAC,SAAS,CAM1B,CACH,CAAC,EAAA,CACE,CACP,EACA,iBAAiB,IAAI,IAAI,GAAG,SAAS,GAAG,CAAC,KACxCA,GAAA,CAAC,UAAU,EAAA,EACT,IAAI,EAAC,aAAa,EAClB,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,WAAW,EACnB,OAAO,EAAE,MACP,OAAO,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,EAEpE,SAAS,EAAC,0FAA0F,EAAA,CACpG,CACH,CAAA,EAAA,CACG,CAAA,EAAA,CACF;AAEV;AAEA,IAAI,CAAC,WAAW,GAAG,gBAAgB;;;;"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { useCall } from '@stream-io/video-react-bindings';
|
|
3
|
+
import '@stream-io/i18n';
|
|
4
|
+
import { useI18n } from '../../../../i18n/TranslationContext.js';
|
|
3
5
|
import { useState, useEffect } from 'react';
|
|
6
|
+
import '@stream-io/i18n/react';
|
|
4
7
|
import { hasScreenShare } from '@stream-io/video-client';
|
|
5
8
|
import 'clsx';
|
|
6
9
|
import '../../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
|
|
7
|
-
import '../../../../components/
|
|
10
|
+
import '../../../../components/Badge/Badge.js';
|
|
11
|
+
import '../../../../components/Button/Button.js';
|
|
8
12
|
import '../../../../components/Button/IconButton.js';
|
|
13
|
+
import '../../../../components/Button/CompositeButton.js';
|
|
9
14
|
import '@floating-ui/react';
|
|
10
15
|
import '../../../../components/CallControls/RecordCallButton.js';
|
|
11
16
|
import '../../../../components/CallControls/ReactionsButton.js';
|
|
@@ -14,12 +19,9 @@ import '../../../../components/DeviceSettings/DeviceSettings.js';
|
|
|
14
19
|
import '../../../../components/DropdownSelect/DropdownSelect.js';
|
|
15
20
|
import '../../../../components/Menu/MenuToggle.js';
|
|
16
21
|
import '../../../../components/CallControls/CancelCallButton.js';
|
|
17
|
-
import '../../../../components/CallControls/CallStatsButton.js';
|
|
18
22
|
import '../../../../components/CallParticipantsList/CallParticipantsList.js';
|
|
19
23
|
import '../../../../components/CallParticipantsList/CallParticipantListingItem.js';
|
|
20
|
-
import '../../../../components/CallStats/CallStats.js';
|
|
21
24
|
import '../../../../components/NoiseCancellation/NoiseCancellationProvider.js';
|
|
22
|
-
import '../../../../components/RingingCall/RingingCall.js';
|
|
23
25
|
import '../../../../components/Permissions/PermissionRequests.js';
|
|
24
26
|
import '../../Video/Video.js';
|
|
25
27
|
import '../../Video/BaseVideo.js';
|
|
@@ -27,6 +29,7 @@ import '../../Video/DefaultVideoPlaceholder.js';
|
|
|
27
29
|
import '../../ParticipantView/ParticipantViewContext.js';
|
|
28
30
|
import { ParticipantView } from '../../ParticipantView/ParticipantView.js';
|
|
29
31
|
import { DefaultParticipantViewUI } from '../../ParticipantView/DefaultParticipantViewUI.js';
|
|
32
|
+
import '../../ParticipantView/ConnectionQualityIndicator.js';
|
|
30
33
|
import { useFilteredParticipants, usePaginatedLayoutSortPreset } from '../hooks.js';
|
|
31
34
|
|
|
32
35
|
const Pip = (props) => {
|
|
@@ -49,7 +52,7 @@ const Pip = (props) => {
|
|
|
49
52
|
if (!call)
|
|
50
53
|
return null;
|
|
51
54
|
return (jsxs("div", { className: "str-video__pip-layout", ref: setLayoutWrapperElement, children: [screenSharingParticipant &&
|
|
52
|
-
(screenSharingParticipant.isLocalParticipant ? (jsxs("div", { className: "str-video__pip-screen-share-local", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__pip-screen-share-local__title", children: t('You are presenting your screen') })] })) : (jsx(ParticipantView, { participant: screenSharingParticipant, trackType: "screenShareTrack", muteAudio: true, mirror: false, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }))), participants.map((participant) => (jsx(ParticipantView, { participant: participant, muteAudio: true, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }, participant.sessionId)))] }));
|
|
55
|
+
(screenSharingParticipant.isLocalParticipant ? (jsxs("div", { className: "str-video__pip-screen-share-local", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__pip-screen-share-local__title", children: t('common.presentingYourScreen.text', 'You are presenting your screen') })] })) : (jsx(ParticipantView, { participant: screenSharingParticipant, trackType: "screenShareTrack", muteAudio: true, mirror: false, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }))), participants.map((participant) => (jsx(ParticipantView, { participant: participant, muteAudio: true, mirror: mirror, VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI }, participant.sessionId)))] }));
|
|
53
56
|
};
|
|
54
57
|
Pip.displayName = 'PipLayout.Pip';
|
|
55
58
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pip.js","sources":["../../../../../../../../../src/core/components/CallLayout/PipLayout/Pip.tsx"],"sourcesContent":["import { useCall
|
|
1
|
+
{"version":3,"file":"Pip.js","sources":["../../../../../../../../../src/core/components/CallLayout/PipLayout/Pip.tsx"],"sourcesContent":["import { useCall } from '@stream-io/video-react-bindings';\nimport { useI18n } from '../../../../i18n';\nimport { useEffect, useState } from 'react';\n\nimport { hasScreenShare } from '@stream-io/video-client';\nimport { Icon } from '../../../../components';\nimport {\n DefaultParticipantViewUI,\n ParticipantView,\n ParticipantViewProps,\n} from '../../ParticipantView';\nimport {\n ParticipantFilter,\n ParticipantPredicate,\n useFilteredParticipants,\n usePaginatedLayoutSortPreset,\n} from '../hooks';\n\nexport type PipLayoutProps = {\n /**\n * Whether to exclude the local participant from the grid.\n * @default false\n */\n excludeLocalParticipant?: boolean;\n\n /**\n * Predicate to filter call participants or a filter object.\n * @example\n * // With a predicate:\n * <PipLayout\n * filterParticipants={p => p.roles.includes('student')}\n * />\n * @example\n * // With a filter object:\n * <PipLayout\n * filterParticipants={{\n * $or: [\n * { roles: { $contains: 'student' } },\n * { isPinned: true },\n * ],\n * }}\n * />\n */\n filterParticipants?: ParticipantPredicate | ParticipantFilter;\n\n /**\n * When set to `false` disables mirroring of the local partipant's video.\n * @default true\n */\n mirrorLocalParticipantVideo?: boolean;\n} & Pick<ParticipantViewProps, 'ParticipantViewUI' | 'VideoPlaceholder'>;\n\nexport const Pip = (props: PipLayoutProps) => {\n const { t } = useI18n();\n const {\n excludeLocalParticipant = false,\n filterParticipants,\n mirrorLocalParticipantVideo = true,\n VideoPlaceholder,\n ParticipantViewUI = DefaultParticipantViewUI,\n } = props;\n const [layoutWrapperElement, setLayoutWrapperElement] =\n useState<HTMLDivElement | null>(null);\n\n const call = useCall();\n const participants = useFilteredParticipants({\n excludeLocalParticipant,\n filterParticipants,\n });\n const screenSharingParticipant = participants.find((p) => hasScreenShare(p));\n\n usePaginatedLayoutSortPreset(call);\n\n useEffect(() => {\n if (!layoutWrapperElement || !call) return;\n return call.setViewport(layoutWrapperElement);\n }, [layoutWrapperElement, call]);\n\n const mirror = mirrorLocalParticipantVideo ? undefined : false;\n\n if (!call) return null;\n\n return (\n <div className=\"str-video__pip-layout\" ref={setLayoutWrapperElement}>\n {screenSharingParticipant &&\n (screenSharingParticipant.isLocalParticipant ? (\n <div className=\"str-video__pip-screen-share-local\">\n <Icon icon=\"screen-share-off\" />\n <span className=\"str-video__pip-screen-share-local__title\">\n {t(\n 'common.presentingYourScreen.text',\n 'You are presenting your screen',\n )}\n </span>\n </div>\n ) : (\n <ParticipantView\n participant={screenSharingParticipant}\n trackType=\"screenShareTrack\"\n muteAudio\n mirror={false}\n VideoPlaceholder={VideoPlaceholder}\n ParticipantViewUI={ParticipantViewUI}\n />\n ))}\n {participants.map((participant) => (\n <ParticipantView\n key={participant.sessionId}\n participant={participant}\n muteAudio\n mirror={mirror}\n VideoPlaceholder={VideoPlaceholder}\n ParticipantViewUI={ParticipantViewUI}\n />\n ))}\n </div>\n );\n};\n\nPip.displayName = 'PipLayout.Pip';\n"],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDO,MAAM,GAAG,GAAG,CAAC,KAAqB,KAAI;AAC3C,IAAA,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE;AACvB,IAAA,MAAM,EACJ,uBAAuB,GAAG,KAAK,EAC/B,kBAAkB,EAClB,2BAA2B,GAAG,IAAI,EAClC,gBAAgB,EAChB,iBAAiB,GAAG,wBAAwB,GAC7C,GAAG,KAAK;IACT,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GACnD,QAAQ,CAAwB,IAAI,CAAC;AAEvC,IAAA,MAAM,IAAI,GAAG,OAAO,EAAE;IACtB,MAAM,YAAY,GAAG,uBAAuB,CAAC;QAC3C,uBAAuB;QACvB,kBAAkB;AACnB,KAAA,CAAC;AACF,IAAA,MAAM,wBAAwB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC;IAE5E,4BAA4B,CAAC,IAAI,CAAC;IAElC,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,oBAAoB,IAAI,CAAC,IAAI;YAAE;AACpC,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,oBAAoB,CAAC;AAC/C,IAAA,CAAC,EAAE,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAG,2BAA2B,GAAG,SAAS,GAAG,KAAK;AAE9D,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,IAAI;IAEtB,QACEA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,uBAAuB,EAAC,GAAG,EAAE,uBAAuB,EAAA,QAAA,EAAA,CAChE,wBAAwB;iBACtB,wBAAwB,CAAC,kBAAkB,IAC1CA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mCAAmC,aAChDC,GAAA,CAAC,IAAI,EAAA,EAAC,IAAI,EAAC,kBAAkB,GAAG,EAChCA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,0CAA0C,EAAA,QAAA,EACvD,CAAC,CACA,kCAAkC,EAClC,gCAAgC,CACjC,GACI,CAAA,EAAA,CACH,KAENA,IAAC,eAAe,EAAA,EACd,WAAW,EAAE,wBAAwB,EACrC,SAAS,EAAC,kBAAkB,EAC5B,SAAS,EAAA,IAAA,EACT,MAAM,EAAE,KAAK,EACb,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,iBAAiB,EAAA,CACpC,CACH,CAAC,EACH,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,MAC5BA,GAAA,CAAC,eAAe,EAAA,EAEd,WAAW,EAAE,WAAW,EACxB,SAAS,EAAA,IAAA,EACT,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,iBAAiB,EAAA,EAL/B,WAAW,CAAC,SAAS,CAM1B,CACH,CAAC,CAAA,EAAA,CACE;AAEV;AAEA,GAAG,CAAC,WAAW,GAAG,eAAe;;;;"}
|
|
@@ -3,13 +3,19 @@ import { useState, useEffect } from 'react';
|
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { hasScreenShare } from '@stream-io/video-client';
|
|
5
5
|
import { useCall, useCallStateHooks, useSpeakerLayoutSortPreset } from '@stream-io/video-react-bindings';
|
|
6
|
+
import '@stream-io/i18n';
|
|
7
|
+
import { useI18n } from '../../../i18n/TranslationContext.js';
|
|
8
|
+
import '@stream-io/i18n/react';
|
|
6
9
|
import '../ParticipantView/ParticipantViewContext.js';
|
|
7
10
|
import '../../../components/Menu/MenuToggle.js';
|
|
8
11
|
import { ParticipantView } from '../ParticipantView/ParticipantView.js';
|
|
9
12
|
import { DefaultParticipantViewUI } from '../ParticipantView/DefaultParticipantViewUI.js';
|
|
13
|
+
import '../ParticipantView/ConnectionQualityIndicator.js';
|
|
10
14
|
import '../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
|
|
11
|
-
import '../../../components/
|
|
15
|
+
import '../../../components/Badge/Badge.js';
|
|
16
|
+
import '../../../components/Button/Button.js';
|
|
12
17
|
import { IconButton } from '../../../components/Button/IconButton.js';
|
|
18
|
+
import '../../../components/Button/CompositeButton.js';
|
|
13
19
|
import '@floating-ui/react';
|
|
14
20
|
import { useVerticalScrollPosition, useHorizontalScrollPosition } from '../../../hooks/useScrollPosition.js';
|
|
15
21
|
import { useDragToScroll } from '../../../hooks/useDragToScroll.js';
|
|
@@ -18,12 +24,9 @@ import '../../../components/CallControls/ReactionsButton.js';
|
|
|
18
24
|
import '../../../components/DeviceSettings/DeviceSettings.js';
|
|
19
25
|
import '../../../components/DropdownSelect/DropdownSelect.js';
|
|
20
26
|
import '../../../components/CallControls/CancelCallButton.js';
|
|
21
|
-
import '../../../components/CallControls/CallStatsButton.js';
|
|
22
27
|
import '../../../components/CallParticipantsList/CallParticipantsList.js';
|
|
23
28
|
import '../../../components/CallParticipantsList/CallParticipantListingItem.js';
|
|
24
|
-
import '../../../components/CallStats/CallStats.js';
|
|
25
29
|
import '../../../components/NoiseCancellation/NoiseCancellationProvider.js';
|
|
26
|
-
import '../../../components/RingingCall/RingingCall.js';
|
|
27
30
|
import '../../../components/Permissions/PermissionRequests.js';
|
|
28
31
|
import '../Video/Video.js';
|
|
29
32
|
import '../Video/BaseVideo.js';
|
|
@@ -79,6 +82,7 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
|
|
|
79
82
|
};
|
|
80
83
|
SpeakerLayout.displayName = 'SpeakerLayout';
|
|
81
84
|
const HorizontalScrollButtons = ({ scrollWrapper, }) => {
|
|
85
|
+
const { t } = useI18n();
|
|
82
86
|
const scrollPosition = useHorizontalScrollPosition(scrollWrapper);
|
|
83
87
|
const scrollStartClickHandler = () => {
|
|
84
88
|
scrollWrapper?.scrollBy({ left: -150, behavior: 'smooth' });
|
|
@@ -86,9 +90,10 @@ const HorizontalScrollButtons = ({ scrollWrapper, }) => {
|
|
|
86
90
|
const scrollEndClickHandler = () => {
|
|
87
91
|
scrollWrapper?.scrollBy({ left: 150, behavior: 'smooth' });
|
|
88
92
|
};
|
|
89
|
-
return (jsxs(Fragment, { children: [scrollPosition && scrollPosition !== 'start' && (jsx(IconButton, { onClick: scrollStartClickHandler, icon: "caret-left", className: "str-video__speaker-layout__participants-bar--button-left" })), scrollPosition && scrollPosition !== 'end' && (jsx(IconButton, { onClick: scrollEndClickHandler, icon: "caret-right", className: "str-video__speaker-layout__participants-bar--button-right" }))] }));
|
|
93
|
+
return (jsxs(Fragment, { children: [scrollPosition && scrollPosition !== 'start' && (jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollStartClickHandler, icon: "caret-left", "aria-label": t('callLayout.speakerLayout.scrollParticipantsLeft.ariaLabel', 'Scroll participants left'), className: "str-video__speaker-layout__participants-bar--button-left" })), scrollPosition && scrollPosition !== 'end' && (jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollEndClickHandler, icon: "caret-right", "aria-label": t('callLayout.speakerLayout.scrollParticipantsRight.ariaLabel', 'Scroll participants right'), className: "str-video__speaker-layout__participants-bar--button-right" }))] }));
|
|
90
94
|
};
|
|
91
95
|
const VerticalScrollButtons = ({ scrollWrapper, }) => {
|
|
96
|
+
const { t } = useI18n();
|
|
92
97
|
const scrollPosition = useVerticalScrollPosition(scrollWrapper);
|
|
93
98
|
const scrollTopClickHandler = () => {
|
|
94
99
|
scrollWrapper?.scrollBy({ top: -150, behavior: 'smooth' });
|
|
@@ -96,7 +101,7 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
|
|
|
96
101
|
const scrollBottomClickHandler = () => {
|
|
97
102
|
scrollWrapper?.scrollBy({ top: 150, behavior: 'smooth' });
|
|
98
103
|
};
|
|
99
|
-
return (jsxs(Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (jsx(IconButton, { onClick: scrollTopClickHandler, icon: "caret-up", className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (jsx(IconButton, { onClick: scrollBottomClickHandler, icon: "caret-down", className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
|
|
104
|
+
return (jsxs(Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollTopClickHandler, icon: "caret-up", "aria-label": t('callLayout.speakerLayout.scrollParticipantsUp.ariaLabel', 'Scroll participants up'), className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (jsx(IconButton, { size: "sm", variant: "secondary", onClick: scrollBottomClickHandler, icon: "caret-down", "aria-label": t('callLayout.speakerLayout.scrollParticipantsDown.ariaLabel', 'Scroll participants down'), className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
export { SpeakerLayout };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpeakerLayout.js","sources":["../../../../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport clsx from 'clsx';\nimport { hasScreenShare } from '@stream-io/video-client';\nimport {\n useCall,\n useCallStateHooks,\n useSpeakerLayoutSortPreset,\n} from '@stream-io/video-react-bindings';\n\nimport {\n DefaultParticipantViewUI,\n ParticipantView,\n ParticipantViewProps,\n} from '../ParticipantView';\nimport { IconButton } from '../../../components';\nimport {\n useDragToScroll,\n useHorizontalScrollPosition,\n useVerticalScrollPosition,\n} from '../../../hooks';\nimport {\n ParticipantFilter,\n ParticipantPredicate,\n useFilteredParticipants,\n useRawRemoteParticipants,\n} from './hooks';\nimport { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';\nimport { ParticipantsAudio } from '../Audio';\n\nexport type SpeakerLayoutProps = {\n /**\n * The UI to be used for the participant in the spotlight.\n */\n ParticipantViewUISpotlight?: ParticipantViewProps['ParticipantViewUI'];\n\n /**\n * The UI to be used for the participants in the participants bar.\n */\n ParticipantViewUIBar?: ParticipantViewProps['ParticipantViewUI'];\n\n /**\n * The position of the participants who are not in focus.\n * Providing `null` will hide the bar.\n */\n participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;\n\n /**\n * Hard limits the number of the participants rendered in the participants bar.\n * Providing string `dynamic` will calculate hard limit based on screen width/height.\n */\n participantsBarLimit?: 'dynamic' | number;\n\n /**\n * When set to `true` will exclude the local participant from layout.\n * @default false\n */\n excludeLocalParticipant?: boolean;\n\n /**\n * Predicate to filter call participants or a filter object.\n * @example\n * // With a predicate:\n * <SpeakerLayout\n * filterParticipants={p => p.roles.includes('student')}\n * />\n * @example\n * // With a filter object:\n * <SpeakerLayout\n * filterParticipants={{\n * $or: [\n * { roles: { $contains: 'student' } },\n * { isPinned: true },\n * ],\n * }}\n * />\n */\n filterParticipants?: ParticipantPredicate | ParticipantFilter;\n\n /**\n * When set to `false` disables mirroring of the local participant's video.\n * @default true\n */\n mirrorLocalParticipantVideo?: boolean;\n\n /**\n * Turns on/off the pagination arrows.\n * @default true\n */\n pageArrowsVisible?: boolean;\n\n /**\n * Whether the layout is muted. Defaults to `false`.\n */\n muted?: boolean;\n\n /**\n * Whether to enable drag-to-scroll functionality on the participants bar.\n * @default false\n */\n enableDragToScroll?: boolean;\n} & Pick<\n ParticipantViewProps,\n 'VideoPlaceholder' | 'PictureInPicturePlaceholder'\n>;\n\nconst DefaultParticipantViewUIBar = () => (\n <DefaultParticipantViewUI menuPlacement=\"top-end\" />\n);\n\nexport const SpeakerLayout = ({\n ParticipantViewUIBar = DefaultParticipantViewUIBar,\n ParticipantViewUISpotlight = DefaultParticipantViewUI,\n VideoPlaceholder,\n PictureInPicturePlaceholder,\n participantsBarPosition = 'bottom',\n participantsBarLimit,\n mirrorLocalParticipantVideo = true,\n excludeLocalParticipant = false,\n filterParticipants,\n pageArrowsVisible = true,\n muted,\n enableDragToScroll = false,\n}: SpeakerLayoutProps) => {\n const call = useCall();\n const { useParticipants } = useCallStateHooks();\n const allParticipants = useParticipants();\n const remoteParticipants = useRawRemoteParticipants();\n const [participantInSpotlight, ...otherParticipants] =\n useFilteredParticipants({ excludeLocalParticipant, filterParticipants });\n const [participantsBarWrapperElement, setParticipantsBarWrapperElement] =\n useState<HTMLDivElement | null>(null);\n const [participantsBarElement, setParticipantsBarElement] =\n useState<HTMLDivElement | null>(null);\n const [buttonsWrapperElement, setButtonsWrapperElement] =\n useState<HTMLDivElement | null>(null);\n\n const isSpeakerScreenSharing =\n participantInSpotlight && hasScreenShare(participantInSpotlight);\n const hardLimit = useCalculateHardLimit(\n buttonsWrapperElement,\n participantsBarElement,\n participantsBarLimit,\n );\n\n const isVertical =\n participantsBarPosition === 'left' || participantsBarPosition === 'right';\n const isHorizontal =\n participantsBarPosition === 'top' || participantsBarPosition === 'bottom';\n\n useEffect(() => {\n if (!participantsBarWrapperElement || !call) return;\n\n const cleanup = call.setViewport(participantsBarWrapperElement);\n\n return () => cleanup?.();\n }, [participantsBarWrapperElement, call]);\n\n const isOneOnOneCall = allParticipants.length === 2;\n useSpeakerLayoutSortPreset(call, isOneOnOneCall);\n useDragToScroll(participantsBarWrapperElement, {\n enabled: enableDragToScroll,\n });\n\n let participantsWithAppliedLimit = otherParticipants;\n\n const hardLimitToApply = isVertical\n ? hardLimit.vertical\n : hardLimit.horizontal;\n\n if (\n typeof participantsBarLimit !== 'undefined' &&\n hardLimitToApply !== null\n ) {\n participantsWithAppliedLimit = otherParticipants.slice(\n 0,\n // subtract 1 if speaker is sharing screen as\n // that one is rendered independently from otherParticipants array\n hardLimitToApply - (isSpeakerScreenSharing ? 1 : 0),\n );\n }\n\n const mirror = mirrorLocalParticipantVideo ? undefined : false;\n\n if (!call) return null;\n\n const renderParticipantsBar =\n participantsBarPosition &&\n (participantsWithAppliedLimit.length > 0 || isSpeakerScreenSharing);\n return (\n <div className=\"str-video__speaker-layout__wrapper\">\n {!muted && <ParticipantsAudio participants={remoteParticipants} />}\n <div\n className={clsx(\n 'str-video__speaker-layout',\n participantsBarPosition &&\n `str-video__speaker-layout--variant-${participantsBarPosition}`,\n )}\n >\n <div className=\"str-video__speaker-layout__spotlight\">\n {participantInSpotlight && (\n <ParticipantView\n participant={participantInSpotlight}\n muteAudio={true}\n mirror={mirror}\n trackType={\n isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack'\n }\n ParticipantViewUI={ParticipantViewUISpotlight}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n />\n )}\n </div>\n {renderParticipantsBar && (\n <div\n ref={setButtonsWrapperElement}\n className=\"str-video__speaker-layout__participants-bar-buttons-wrapper\"\n >\n <div\n className=\"str-video__speaker-layout__participants-bar-wrapper\"\n ref={setParticipantsBarWrapperElement}\n >\n <div\n ref={setParticipantsBarElement}\n className=\"str-video__speaker-layout__participants-bar\"\n >\n {isSpeakerScreenSharing && (\n <div\n className=\"str-video__speaker-layout__participant-tile\"\n key={participantInSpotlight.sessionId}\n >\n <ParticipantView\n participant={participantInSpotlight}\n ParticipantViewUI={ParticipantViewUIBar}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n mirror={mirror}\n muteAudio={true}\n />\n </div>\n )}\n {participantsWithAppliedLimit.map((participant) => (\n <div\n className=\"str-video__speaker-layout__participant-tile\"\n key={participant.sessionId}\n >\n <ParticipantView\n participant={participant}\n ParticipantViewUI={ParticipantViewUIBar}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n mirror={mirror}\n muteAudio={true}\n />\n </div>\n ))}\n </div>\n </div>\n {pageArrowsVisible && isVertical && (\n <VerticalScrollButtons\n scrollWrapper={participantsBarWrapperElement}\n />\n )}\n {pageArrowsVisible && isHorizontal && (\n <HorizontalScrollButtons\n scrollWrapper={participantsBarWrapperElement}\n />\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n\nSpeakerLayout.displayName = 'SpeakerLayout';\n\ntype ScrollButtonsProps<T extends HTMLElement> = {\n scrollWrapper: T | null;\n};\n\nconst HorizontalScrollButtons = <T extends HTMLElement>({\n scrollWrapper,\n}: ScrollButtonsProps<T>) => {\n const scrollPosition = useHorizontalScrollPosition(scrollWrapper);\n\n const scrollStartClickHandler = () => {\n scrollWrapper?.scrollBy({ left: -150, behavior: 'smooth' });\n };\n\n const scrollEndClickHandler = () => {\n scrollWrapper?.scrollBy({ left: 150, behavior: 'smooth' });\n };\n return (\n <>\n {scrollPosition && scrollPosition !== 'start' && (\n <IconButton\n onClick={scrollStartClickHandler}\n icon=\"caret-left\"\n className=\"str-video__speaker-layout__participants-bar--button-left\"\n />\n )}\n {scrollPosition && scrollPosition !== 'end' && (\n <IconButton\n onClick={scrollEndClickHandler}\n icon=\"caret-right\"\n className=\"str-video__speaker-layout__participants-bar--button-right\"\n />\n )}\n </>\n );\n};\n\nconst VerticalScrollButtons = <T extends HTMLElement>({\n scrollWrapper,\n}: ScrollButtonsProps<T>) => {\n const scrollPosition = useVerticalScrollPosition(scrollWrapper);\n\n const scrollTopClickHandler = () => {\n scrollWrapper?.scrollBy({ top: -150, behavior: 'smooth' });\n };\n\n const scrollBottomClickHandler = () => {\n scrollWrapper?.scrollBy({ top: 150, behavior: 'smooth' });\n };\n return (\n <>\n {scrollPosition && scrollPosition !== 'top' && (\n <IconButton\n onClick={scrollTopClickHandler}\n icon=\"caret-up\"\n className=\"str-video__speaker-layout__participants-bar--button-top\"\n />\n )}\n {scrollPosition && scrollPosition !== 'bottom' && (\n <IconButton\n onClick={scrollBottomClickHandler}\n icon=\"caret-down\"\n className=\"str-video__speaker-layout__participants-bar--button-bottom\"\n />\n )}\n </>\n );\n};\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SpeakerLayout.js","sources":["../../../../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport clsx from 'clsx';\nimport { hasScreenShare } from '@stream-io/video-client';\nimport {\n useCall,\n useCallStateHooks,\n useSpeakerLayoutSortPreset,\n} from '@stream-io/video-react-bindings';\nimport { useI18n } from '../../../i18n';\n\nimport {\n DefaultParticipantViewUI,\n ParticipantView,\n ParticipantViewProps,\n} from '../ParticipantView';\nimport { IconButton } from '../../../components';\nimport {\n useDragToScroll,\n useHorizontalScrollPosition,\n useVerticalScrollPosition,\n} from '../../../hooks';\nimport {\n ParticipantFilter,\n ParticipantPredicate,\n useFilteredParticipants,\n useRawRemoteParticipants,\n} from './hooks';\nimport { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';\nimport { ParticipantsAudio } from '../Audio';\n\nexport type SpeakerLayoutProps = {\n /**\n * The UI to be used for the participant in the spotlight.\n */\n ParticipantViewUISpotlight?: ParticipantViewProps['ParticipantViewUI'];\n\n /**\n * The UI to be used for the participants in the participants bar.\n */\n ParticipantViewUIBar?: ParticipantViewProps['ParticipantViewUI'];\n\n /**\n * The position of the participants who are not in focus.\n * Providing `null` will hide the bar.\n */\n participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;\n\n /**\n * Hard limits the number of the participants rendered in the participants bar.\n * Providing string `dynamic` will calculate hard limit based on screen width/height.\n */\n participantsBarLimit?: 'dynamic' | number;\n\n /**\n * When set to `true` will exclude the local participant from layout.\n * @default false\n */\n excludeLocalParticipant?: boolean;\n\n /**\n * Predicate to filter call participants or a filter object.\n * @example\n * // With a predicate:\n * <SpeakerLayout\n * filterParticipants={p => p.roles.includes('student')}\n * />\n * @example\n * // With a filter object:\n * <SpeakerLayout\n * filterParticipants={{\n * $or: [\n * { roles: { $contains: 'student' } },\n * { isPinned: true },\n * ],\n * }}\n * />\n */\n filterParticipants?: ParticipantPredicate | ParticipantFilter;\n\n /**\n * When set to `false` disables mirroring of the local participant's video.\n * @default true\n */\n mirrorLocalParticipantVideo?: boolean;\n\n /**\n * Turns on/off the pagination arrows.\n * @default true\n */\n pageArrowsVisible?: boolean;\n\n /**\n * Whether the layout is muted. Defaults to `false`.\n */\n muted?: boolean;\n\n /**\n * Whether to enable drag-to-scroll functionality on the participants bar.\n * @default false\n */\n enableDragToScroll?: boolean;\n} & Pick<\n ParticipantViewProps,\n 'VideoPlaceholder' | 'PictureInPicturePlaceholder'\n>;\n\nconst DefaultParticipantViewUIBar = () => (\n <DefaultParticipantViewUI menuPlacement=\"top-end\" />\n);\n\nexport const SpeakerLayout = ({\n ParticipantViewUIBar = DefaultParticipantViewUIBar,\n ParticipantViewUISpotlight = DefaultParticipantViewUI,\n VideoPlaceholder,\n PictureInPicturePlaceholder,\n participantsBarPosition = 'bottom',\n participantsBarLimit,\n mirrorLocalParticipantVideo = true,\n excludeLocalParticipant = false,\n filterParticipants,\n pageArrowsVisible = true,\n muted,\n enableDragToScroll = false,\n}: SpeakerLayoutProps) => {\n const call = useCall();\n const { useParticipants } = useCallStateHooks();\n const allParticipants = useParticipants();\n const remoteParticipants = useRawRemoteParticipants();\n const [participantInSpotlight, ...otherParticipants] =\n useFilteredParticipants({ excludeLocalParticipant, filterParticipants });\n const [participantsBarWrapperElement, setParticipantsBarWrapperElement] =\n useState<HTMLDivElement | null>(null);\n const [participantsBarElement, setParticipantsBarElement] =\n useState<HTMLDivElement | null>(null);\n const [buttonsWrapperElement, setButtonsWrapperElement] =\n useState<HTMLDivElement | null>(null);\n\n const isSpeakerScreenSharing =\n participantInSpotlight && hasScreenShare(participantInSpotlight);\n const hardLimit = useCalculateHardLimit(\n buttonsWrapperElement,\n participantsBarElement,\n participantsBarLimit,\n );\n\n const isVertical =\n participantsBarPosition === 'left' || participantsBarPosition === 'right';\n const isHorizontal =\n participantsBarPosition === 'top' || participantsBarPosition === 'bottom';\n\n useEffect(() => {\n if (!participantsBarWrapperElement || !call) return;\n\n const cleanup = call.setViewport(participantsBarWrapperElement);\n\n return () => cleanup?.();\n }, [participantsBarWrapperElement, call]);\n\n const isOneOnOneCall = allParticipants.length === 2;\n useSpeakerLayoutSortPreset(call, isOneOnOneCall);\n useDragToScroll(participantsBarWrapperElement, {\n enabled: enableDragToScroll,\n });\n\n let participantsWithAppliedLimit = otherParticipants;\n\n const hardLimitToApply = isVertical\n ? hardLimit.vertical\n : hardLimit.horizontal;\n\n if (\n typeof participantsBarLimit !== 'undefined' &&\n hardLimitToApply !== null\n ) {\n participantsWithAppliedLimit = otherParticipants.slice(\n 0,\n // subtract 1 if speaker is sharing screen as\n // that one is rendered independently from otherParticipants array\n hardLimitToApply - (isSpeakerScreenSharing ? 1 : 0),\n );\n }\n\n const mirror = mirrorLocalParticipantVideo ? undefined : false;\n\n if (!call) return null;\n\n const renderParticipantsBar =\n participantsBarPosition &&\n (participantsWithAppliedLimit.length > 0 || isSpeakerScreenSharing);\n return (\n <div className=\"str-video__speaker-layout__wrapper\">\n {!muted && <ParticipantsAudio participants={remoteParticipants} />}\n <div\n className={clsx(\n 'str-video__speaker-layout',\n participantsBarPosition &&\n `str-video__speaker-layout--variant-${participantsBarPosition}`,\n )}\n >\n <div className=\"str-video__speaker-layout__spotlight\">\n {participantInSpotlight && (\n <ParticipantView\n participant={participantInSpotlight}\n muteAudio={true}\n mirror={mirror}\n trackType={\n isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack'\n }\n ParticipantViewUI={ParticipantViewUISpotlight}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n />\n )}\n </div>\n {renderParticipantsBar && (\n <div\n ref={setButtonsWrapperElement}\n className=\"str-video__speaker-layout__participants-bar-buttons-wrapper\"\n >\n <div\n className=\"str-video__speaker-layout__participants-bar-wrapper\"\n ref={setParticipantsBarWrapperElement}\n >\n <div\n ref={setParticipantsBarElement}\n className=\"str-video__speaker-layout__participants-bar\"\n >\n {isSpeakerScreenSharing && (\n <div\n className=\"str-video__speaker-layout__participant-tile\"\n key={participantInSpotlight.sessionId}\n >\n <ParticipantView\n participant={participantInSpotlight}\n ParticipantViewUI={ParticipantViewUIBar}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n mirror={mirror}\n muteAudio={true}\n />\n </div>\n )}\n {participantsWithAppliedLimit.map((participant) => (\n <div\n className=\"str-video__speaker-layout__participant-tile\"\n key={participant.sessionId}\n >\n <ParticipantView\n participant={participant}\n ParticipantViewUI={ParticipantViewUIBar}\n VideoPlaceholder={VideoPlaceholder}\n PictureInPicturePlaceholder={PictureInPicturePlaceholder}\n mirror={mirror}\n muteAudio={true}\n />\n </div>\n ))}\n </div>\n </div>\n {pageArrowsVisible && isVertical && (\n <VerticalScrollButtons\n scrollWrapper={participantsBarWrapperElement}\n />\n )}\n {pageArrowsVisible && isHorizontal && (\n <HorizontalScrollButtons\n scrollWrapper={participantsBarWrapperElement}\n />\n )}\n </div>\n )}\n </div>\n </div>\n );\n};\n\nSpeakerLayout.displayName = 'SpeakerLayout';\n\ntype ScrollButtonsProps<T extends HTMLElement> = {\n scrollWrapper: T | null;\n};\n\nconst HorizontalScrollButtons = <T extends HTMLElement>({\n scrollWrapper,\n}: ScrollButtonsProps<T>) => {\n const { t } = useI18n();\n const scrollPosition = useHorizontalScrollPosition(scrollWrapper);\n\n const scrollStartClickHandler = () => {\n scrollWrapper?.scrollBy({ left: -150, behavior: 'smooth' });\n };\n\n const scrollEndClickHandler = () => {\n scrollWrapper?.scrollBy({ left: 150, behavior: 'smooth' });\n };\n return (\n <>\n {scrollPosition && scrollPosition !== 'start' && (\n <IconButton\n size=\"sm\"\n variant=\"secondary\"\n onClick={scrollStartClickHandler}\n icon=\"caret-left\"\n aria-label={t(\n 'callLayout.speakerLayout.scrollParticipantsLeft.ariaLabel',\n 'Scroll participants left',\n )}\n className=\"str-video__speaker-layout__participants-bar--button-left\"\n />\n )}\n {scrollPosition && scrollPosition !== 'end' && (\n <IconButton\n size=\"sm\"\n variant=\"secondary\"\n onClick={scrollEndClickHandler}\n icon=\"caret-right\"\n aria-label={t(\n 'callLayout.speakerLayout.scrollParticipantsRight.ariaLabel',\n 'Scroll participants right',\n )}\n className=\"str-video__speaker-layout__participants-bar--button-right\"\n />\n )}\n </>\n );\n};\n\nconst VerticalScrollButtons = <T extends HTMLElement>({\n scrollWrapper,\n}: ScrollButtonsProps<T>) => {\n const { t } = useI18n();\n const scrollPosition = useVerticalScrollPosition(scrollWrapper);\n\n const scrollTopClickHandler = () => {\n scrollWrapper?.scrollBy({ top: -150, behavior: 'smooth' });\n };\n\n const scrollBottomClickHandler = () => {\n scrollWrapper?.scrollBy({ top: 150, behavior: 'smooth' });\n };\n return (\n <>\n {scrollPosition && scrollPosition !== 'top' && (\n <IconButton\n size=\"sm\"\n variant=\"secondary\"\n onClick={scrollTopClickHandler}\n icon=\"caret-up\"\n aria-label={t(\n 'callLayout.speakerLayout.scrollParticipantsUp.ariaLabel',\n 'Scroll participants up',\n )}\n className=\"str-video__speaker-layout__participants-bar--button-top\"\n />\n )}\n {scrollPosition && scrollPosition !== 'bottom' && (\n <IconButton\n size=\"sm\"\n variant=\"secondary\"\n onClick={scrollBottomClickHandler}\n icon=\"caret-down\"\n aria-label={t(\n 'callLayout.speakerLayout.scrollParticipantsDown.ariaLabel',\n 'Scroll participants down',\n )}\n className=\"str-video__speaker-layout__participants-bar--button-bottom\"\n />\n )}\n </>\n );\n};\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { SfuModels } from '@stream-io/video-client';
|
|
4
|
+
|
|
5
|
+
/** How many of the three bars are lit, per quality level. */
|
|
6
|
+
const ACTIVE_BARS = {
|
|
7
|
+
[SfuModels.ConnectionQuality.POOR]: 1,
|
|
8
|
+
[SfuModels.ConnectionQuality.GOOD]: 2,
|
|
9
|
+
[SfuModels.ConnectionQuality.EXCELLENT]: 3,
|
|
10
|
+
};
|
|
11
|
+
const BAR_COUNT = 3;
|
|
12
|
+
/**
|
|
13
|
+
* Three ascending bars showing a participant's connection quality.
|
|
14
|
+
*/
|
|
15
|
+
const ConnectionQualityIndicator = ({ quality, title, className, }) => {
|
|
16
|
+
const activeBars = ACTIVE_BARS[quality];
|
|
17
|
+
if (!activeBars)
|
|
18
|
+
return null;
|
|
19
|
+
const level = SfuModels.ConnectionQuality[quality].toLowerCase();
|
|
20
|
+
return (jsx("span", { className: clsx('str-video__connection-quality-indicator', `str-video__connection-quality-indicator--${level}`, className), title: title ?? level, children: Array.from({ length: BAR_COUNT }, (_, index) => (jsx("span", { className: clsx('str-video__connection-quality-indicator__bar', index < activeBars &&
|
|
21
|
+
'str-video__connection-quality-indicator__bar--active') }, index))) }));
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { ConnectionQualityIndicator };
|
|
25
|
+
//# sourceMappingURL=ConnectionQualityIndicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectionQualityIndicator.js","sources":["../../../../../../../../src/core/components/ParticipantView/ConnectionQualityIndicator.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport { SfuModels } from '@stream-io/video-client';\n\nexport type ConnectionQualityIndicatorProps = {\n quality: SfuModels.ConnectionQuality;\n title?: string;\n className?: string;\n};\n\n/** How many of the three bars are lit, per quality level. */\nconst ACTIVE_BARS: Partial<Record<SfuModels.ConnectionQuality, number>> = {\n [SfuModels.ConnectionQuality.POOR]: 1,\n [SfuModels.ConnectionQuality.GOOD]: 2,\n [SfuModels.ConnectionQuality.EXCELLENT]: 3,\n};\n\nconst BAR_COUNT = 3;\n\n/**\n * Three ascending bars showing a participant's connection quality.\n */\nexport const ConnectionQualityIndicator = ({\n quality,\n title,\n className,\n}: ConnectionQualityIndicatorProps) => {\n const activeBars = ACTIVE_BARS[quality];\n\n if (!activeBars) return null;\n\n const level = SfuModels.ConnectionQuality[quality].toLowerCase();\n\n return (\n <span\n className={clsx(\n 'str-video__connection-quality-indicator',\n `str-video__connection-quality-indicator--${level}`,\n className,\n )}\n title={title ?? level}\n >\n {Array.from({ length: BAR_COUNT }, (_, index) => (\n <span\n key={index}\n className={clsx(\n 'str-video__connection-quality-indicator__bar',\n index < activeBars &&\n 'str-video__connection-quality-indicator__bar--active',\n )}\n />\n ))}\n </span>\n );\n};\n"],"names":["_jsx"],"mappings":";;;;AASA;AACA,MAAM,WAAW,GAAyD;AACxE,IAAA,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC;AACrC,IAAA,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC;AACrC,IAAA,CAAC,SAAS,CAAC,iBAAiB,CAAC,SAAS,GAAG,CAAC;CAC3C;AAED,MAAM,SAAS,GAAG,CAAC;AAEnB;;AAEG;AACI,MAAM,0BAA0B,GAAG,CAAC,EACzC,OAAO,EACP,KAAK,EACL,SAAS,GACuB,KAAI;AACpC,IAAA,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC;AAEvC,IAAA,IAAI,CAAC,UAAU;AAAE,QAAA,OAAO,IAAI;IAE5B,MAAM,KAAK,GAAG,SAAS,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;IAEhE,QACEA,cACE,SAAS,EAAE,IAAI,CACb,yCAAyC,EACzC,CAAA,yCAAA,EAA4C,KAAK,CAAA,CAAE,EACnD,SAAS,CACV,EACD,KAAK,EAAE,KAAK,IAAI,KAAK,EAAA,QAAA,EAEpB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,MAC1CA,GAAA,CAAA,MAAA,EAAA,EAEE,SAAS,EAAE,IAAI,CACb,8CAA8C,EAC9C,KAAK,GAAG,UAAU;gBAChB,sDAAsD,CACzD,IALI,KAAK,CAMV,CACH,CAAC,EAAA,CACG;AAEX;;;;"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
|
-
import { hasScreenShare,
|
|
4
|
-
import { useCallStateHooks, useCall,
|
|
3
|
+
import { hasScreenShare, hasAudio, hasVideo, hasPausedTrack, SfuModels } from '@stream-io/video-client';
|
|
4
|
+
import { useCallStateHooks, useCall, useIsAudioConnecting, useIsVideoConnecting } from '@stream-io/video-react-bindings';
|
|
5
|
+
import '@stream-io/i18n';
|
|
6
|
+
import { useI18n } from '../../../i18n/TranslationContext.js';
|
|
7
|
+
import '@stream-io/i18n/react';
|
|
5
8
|
import clsx from 'clsx';
|
|
6
9
|
import '../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
|
|
7
|
-
import '../../../components/
|
|
10
|
+
import '../../../components/Badge/Badge.js';
|
|
11
|
+
import { Button } from '../../../components/Button/Button.js';
|
|
8
12
|
import { IconButton } from '../../../components/Button/IconButton.js';
|
|
13
|
+
import '../../../components/Button/CompositeButton.js';
|
|
9
14
|
import { Notification } from '../../../components/Notification/Notification.js';
|
|
10
15
|
import '../../../components/CallControls/RecordCallButton.js';
|
|
11
16
|
import '../../../components/CallControls/ReactionsButton.js';
|
|
@@ -15,23 +20,22 @@ import '../../../components/DeviceSettings/DeviceSettings.js';
|
|
|
15
20
|
import '../../../components/DropdownSelect/DropdownSelect.js';
|
|
16
21
|
import { MenuToggle } from '../../../components/Menu/MenuToggle.js';
|
|
17
22
|
import '../../../components/CallControls/CancelCallButton.js';
|
|
18
|
-
import '../../../components/CallControls/CallStatsButton.js';
|
|
19
23
|
import '../../../components/CallParticipantsList/CallParticipantsList.js';
|
|
20
24
|
import '../../../components/CallParticipantsList/CallParticipantListingItem.js';
|
|
21
25
|
import { LoadingIndicator } from '../../../components/LoadingIndicator/LoadingIndicator.js';
|
|
22
|
-
import '../../../components/CallStats/CallStats.js';
|
|
23
26
|
import '../../../components/NoiseCancellation/NoiseCancellationProvider.js';
|
|
24
27
|
import { Reaction } from '../../../components/Reaction/Reaction.js';
|
|
25
|
-
import '../../../components/RingingCall/RingingCall.js';
|
|
26
28
|
import '../../../components/Permissions/PermissionRequests.js';
|
|
27
29
|
import '../Video/Video.js';
|
|
28
30
|
import '../Video/BaseVideo.js';
|
|
29
31
|
import '../Video/DefaultVideoPlaceholder.js';
|
|
30
32
|
import { ParticipantActionsContextMenu } from './ParticipantActionsContextMenu.js';
|
|
33
|
+
import { ConnectionQualityIndicator } from './ConnectionQualityIndicator.js';
|
|
31
34
|
import { useParticipantViewContext } from './ParticipantViewContext.js';
|
|
32
35
|
|
|
33
36
|
const ToggleButton = forwardRef(function ToggleButtonRender(props, ref) {
|
|
34
|
-
|
|
37
|
+
const { t } = useI18n();
|
|
38
|
+
return (jsx(IconButton, { className: "str-video__participant-view__menu-button", active: props.menuShown, size: "sm", variant: "secondary", ref: ref, title: t('participantView.moreOptions.title', 'More options'), icon: "ellipsis" }));
|
|
35
39
|
});
|
|
36
40
|
const DefaultScreenShareOverlay = () => {
|
|
37
41
|
const call = useCall();
|
|
@@ -41,7 +45,7 @@ const DefaultScreenShareOverlay = () => {
|
|
|
41
45
|
console.error('Failed to stop screen sharing:', err);
|
|
42
46
|
});
|
|
43
47
|
};
|
|
44
|
-
return (jsxs("div", { className: "str-video__screen-share-overlay", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__screen-share-overlay__title", children: t('You are presenting your screen') }), jsxs(
|
|
48
|
+
return (jsxs("div", { className: "str-video__screen-share-overlay", children: [jsx(Icon, { icon: "screen-share-off" }), jsx("span", { className: "str-video__screen-share-overlay__title", children: t('common.presentingYourScreen.text', 'You are presenting your screen') }), jsxs(Button, { variant: "destructive", onClick: stopScreenShare, className: "str-video__screen-share-overlay__button", children: [jsx(Icon, { icon: "close" }), ' ', t('participantView.stopScreenSharing.label', 'Stop Screen Sharing')] })] }));
|
|
45
49
|
};
|
|
46
50
|
/**
|
|
47
51
|
* Rendered over a remote participant's tile when the browser's autoplay policy
|
|
@@ -51,7 +55,7 @@ const DefaultScreenShareOverlay = () => {
|
|
|
51
55
|
const DefaultAudioBlockedNotification = () => {
|
|
52
56
|
const call = useCall();
|
|
53
57
|
const { t } = useI18n();
|
|
54
|
-
return (jsx("button", { type: "button", className: "str-video__audio-blocked-notification", onClick: call?.resumeAudio, "aria-live": "polite", children: jsxs("span", { className: "str-video__audio-blocked-notification__message", children: [jsx(Icon, { icon: "speaker" }), jsxs("span", { className: "str-video__audio-blocked-notification__text", children: [jsx("span", { className: "str-video__audio-blocked-notification__title", children: t('Audio is blocked by your system') }), jsx("span", { className: "str-video__audio-blocked-notification__subtitle", children: t('Click or tap to play audio') })] })] }) }));
|
|
58
|
+
return (jsx("button", { type: "button", className: "str-video__audio-blocked-notification", onClick: call?.resumeAudio, "aria-live": "polite", children: jsxs("span", { className: "str-video__audio-blocked-notification__message", children: [jsx(Icon, { icon: "speaker" }), jsxs("span", { className: "str-video__audio-blocked-notification__text", children: [jsx("span", { className: "str-video__audio-blocked-notification__title", children: t('participantView.audioBlocked.title', 'Audio is blocked by your system') }), jsx("span", { className: "str-video__audio-blocked-notification__subtitle", children: t('participantView.audioBlocked.description', 'Click or tap to play audio') })] })] }) }));
|
|
55
59
|
};
|
|
56
60
|
const DefaultParticipantViewUI = ({ indicatorsVisible = true, menuPlacement = 'bottom-start', showMenuButton = true, ParticipantActionsContextMenu: ParticipantActionsContextMenu$1 = ParticipantActionsContextMenu, AudioBlockedNotification = DefaultAudioBlockedNotification, }) => {
|
|
57
61
|
const { participant, trackType } = useParticipantViewContext();
|
|
@@ -73,16 +77,16 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
|
|
|
73
77
|
const { isLocalParticipant, connectionQuality, pin, sessionId, name, userId, } = participant;
|
|
74
78
|
const call = useCall();
|
|
75
79
|
const { t } = useI18n();
|
|
76
|
-
const connectionQualityAsString = !!connectionQuality &&
|
|
77
|
-
SfuModels.ConnectionQuality[connectionQuality].toLowerCase();
|
|
78
80
|
const hasAudioTrack = hasAudio(participant);
|
|
79
81
|
const hasVideoTrack = hasVideo(participant);
|
|
80
82
|
const canUnpin = !!pin && pin.isLocalPin;
|
|
81
83
|
const isTrackPaused = trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;
|
|
82
84
|
const isAudioConnecting = useIsAudioConnecting(participant);
|
|
83
85
|
const isVideoConnecting = useIsVideoConnecting(participant);
|
|
84
|
-
return (jsxs(Fragment, { children: [jsx("div", { className: "str-video__participant-details", children: jsxs("div", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isAudioConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && isVideoConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--video-connecting", tooltip: t('Video is connecting...') })), indicatorsVisible && !hasVideoTrack && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsx("span", { title: canUnpin
|
|
85
|
-
|
|
86
|
+
return (jsxs(Fragment, { children: [jsx("div", { className: "str-video__participant-details", children: jsxs("div", { className: "str-video__participant-details__name", children: [jsx("span", { className: "str-video__participant-details__name-text", children: name || userId }), indicatorsVisible && isAudioConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('participantView.audioConnecting.title', 'Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && isVideoConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--video-connecting", tooltip: t('participantView.videoConnecting.title', 'Video is connecting...') })), indicatorsVisible && !hasVideoTrack && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsx("span", { title: t('participantView.videoPausedInsufficientBandwidth.title', 'Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsx("span", { title: canUnpin
|
|
87
|
+
? t('participantView.unpin.label', 'Unpin')
|
|
88
|
+
: t('participantList.pinned.title', 'Pinned'), onClick: canUnpin ? () => call?.unpin(sessionId) : undefined, className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsx(Notification, { state: "error", isVisible: isLocalParticipant &&
|
|
89
|
+
connectionQuality === SfuModels.ConnectionQuality.POOR, message: t('participantView.connectionQuality.poor.text', 'Poor connection quality. Please check your internet connection.'), children: connectionQuality && (jsx(ConnectionQualityIndicator, { quality: connectionQuality })) }))] }));
|
|
86
90
|
};
|
|
87
91
|
const SpeechIndicator = () => {
|
|
88
92
|
const { participant } = useParticipantViewContext();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultParticipantViewUI.js","sources":["../../../../../../../../src/core/components/ParticipantView/DefaultParticipantViewUI.tsx"],"sourcesContent":["import { ComponentType, forwardRef } from 'react';\nimport { Placement } from '@floating-ui/react';\nimport {\n hasAudio,\n hasPausedTrack,\n hasScreenShare,\n hasVideo,\n SfuModels,\n} from '@stream-io/video-client';\nimport {\n useCall,\n useCallStateHooks,\n useI18n,\n useIsAudioConnecting,\n useIsVideoConnecting,\n} from '@stream-io/video-react-bindings';\nimport clsx from 'clsx';\n\nimport {\n Icon,\n IconButton,\n LoadingIndicator,\n MenuToggle,\n Notification,\n ToggleMenuButtonProps,\n} from '../../../components';\nimport { ParticipantActionsContextMenu as DefaultParticipantActionsContextMenu } from './ParticipantActionsContextMenu';\nimport { Reaction } from '../../../components/Reaction';\nimport { useParticipantViewContext } from './ParticipantViewContext';\n\nexport type DefaultParticipantViewUIProps = {\n /**\n * Turns on/off the status indicator icons (mute, connection quality, etc...).\n */\n indicatorsVisible?: boolean;\n /**\n * Placement of the context menu component when opened\n */\n menuPlacement?: Placement;\n /**\n * Option to show/hide menu button component\n */\n showMenuButton?: boolean;\n /**\n * Custom component to render the context menu\n */\n ParticipantActionsContextMenu?: ComponentType;\n /**\n * Rendered over the tile when this participant's audio is blocked by the\n * browser's autoplay policy. Selecting it calls {@link Call.resumeAudio} to\n * unblock audio. Defaults to\n * {@link DefaultAudioBlockedNotification}; pass `null` to disable.\n */\n AudioBlockedNotification?: ComponentType | null;\n};\n\nconst ToggleButton = forwardRef<HTMLButtonElement, ToggleMenuButtonProps>(\n function ToggleButtonRender(props, ref) {\n return <IconButton enabled={props.menuShown} icon=\"ellipsis\" ref={ref} />;\n },\n);\n\nexport const DefaultScreenShareOverlay = () => {\n const call = useCall();\n const { t } = useI18n();\n\n const stopScreenShare = () => {\n call?.screenShare.disable().catch((err) => {\n console.error('Failed to stop screen sharing:', err);\n });\n };\n\n return (\n <div className=\"str-video__screen-share-overlay\">\n <Icon icon=\"screen-share-off\" />\n <span className=\"str-video__screen-share-overlay__title\">\n {t('You are presenting your screen')}\n </span>\n <button\n onClick={stopScreenShare}\n type=\"button\"\n className=\"str-video__screen-share-overlay__button\"\n >\n <Icon icon=\"close\" /> {t('Stop Screen Sharing')}\n </button>\n </div>\n );\n};\n\n/**\n * Rendered over a remote participant's tile when the browser's autoplay policy\n * has blocked their audio. Selecting it calls `call.resumeAudio()`, unblocking\n * every blocked element at once.\n */\nexport const DefaultAudioBlockedNotification = () => {\n const call = useCall();\n const { t } = useI18n();\n\n return (\n <button\n type=\"button\"\n className=\"str-video__audio-blocked-notification\"\n onClick={call?.resumeAudio}\n aria-live=\"polite\"\n >\n <span className=\"str-video__audio-blocked-notification__message\">\n <Icon icon=\"speaker\" />\n <span className=\"str-video__audio-blocked-notification__text\">\n <span className=\"str-video__audio-blocked-notification__title\">\n {t('Audio is blocked by your system')}\n </span>\n <span className=\"str-video__audio-blocked-notification__subtitle\">\n {t('Click or tap to play audio')}\n </span>\n </span>\n </span>\n </button>\n );\n};\n\nexport const DefaultParticipantViewUI = ({\n indicatorsVisible = true,\n menuPlacement = 'bottom-start',\n showMenuButton = true,\n ParticipantActionsContextMenu = DefaultParticipantActionsContextMenu,\n AudioBlockedNotification = DefaultAudioBlockedNotification,\n}: DefaultParticipantViewUIProps) => {\n const { participant, trackType } = useParticipantViewContext();\n const { useAutoplayBlockedSessionIds } = useCallStateHooks();\n const blockedSessionIds = useAutoplayBlockedSessionIds();\n const isScreenSharing = hasScreenShare(participant);\n const shouldShowAudioBlockedNotification =\n !participant.isLocalParticipant &&\n trackType !== 'screenShareTrack' &&\n blockedSessionIds.includes(participant.sessionId);\n\n if (\n participant.isLocalParticipant &&\n isScreenSharing &&\n trackType === 'screenShareTrack'\n ) {\n return (\n <>\n <DefaultScreenShareOverlay />\n <ParticipantDetails indicatorsVisible={indicatorsVisible} />\n </>\n );\n }\n\n return (\n <>\n {showMenuButton && (\n <MenuToggle\n strategy=\"fixed\"\n placement={menuPlacement}\n ToggleButton={ToggleButton}\n >\n <ParticipantActionsContextMenu />\n </MenuToggle>\n )}\n <Reaction participant={participant} />\n <ParticipantDetails indicatorsVisible={indicatorsVisible} />\n {AudioBlockedNotification && shouldShowAudioBlockedNotification && (\n <AudioBlockedNotification />\n )}\n </>\n );\n};\n\nexport const ParticipantDetails = ({\n indicatorsVisible = true,\n}: Pick<DefaultParticipantViewUIProps, 'indicatorsVisible'>) => {\n const { participant, trackType } = useParticipantViewContext();\n const {\n isLocalParticipant,\n connectionQuality,\n pin,\n sessionId,\n name,\n userId,\n } = participant;\n const call = useCall();\n\n const { t } = useI18n();\n const connectionQualityAsString =\n !!connectionQuality &&\n SfuModels.ConnectionQuality[connectionQuality].toLowerCase();\n\n const hasAudioTrack = hasAudio(participant);\n const hasVideoTrack = hasVideo(participant);\n const canUnpin = !!pin && pin.isLocalPin;\n const isTrackPaused =\n trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;\n\n const isAudioConnecting = useIsAudioConnecting(participant);\n const isVideoConnecting = useIsVideoConnecting(participant);\n\n return (\n <>\n <div className=\"str-video__participant-details\">\n <div className=\"str-video__participant-details__name\">\n {name || userId}\n {indicatorsVisible && isAudioConnecting && (\n <LoadingIndicator\n className=\"str-video__participant-details__name--audio-connecting\"\n tooltip={t('Audio is connecting...')}\n />\n )}\n {indicatorsVisible && !hasAudioTrack && (\n <span className=\"str-video__participant-details__name--audio-muted\" />\n )}\n {indicatorsVisible && isVideoConnecting && (\n <LoadingIndicator\n className=\"str-video__participant-details__name--video-connecting\"\n tooltip={t('Video is connecting...')}\n />\n )}\n {indicatorsVisible && !hasVideoTrack && (\n <span className=\"str-video__participant-details__name--video-muted\" />\n )}\n {indicatorsVisible && isTrackPaused && (\n <span\n title={t('Video paused due to insufficient bandwidth')}\n className=\"str-video__participant-details__name--track-paused\"\n />\n )}\n {indicatorsVisible && pin && (\n <span\n title={canUnpin ? t('Unpin') : t('Pinned')}\n onClick={canUnpin ? () => call?.unpin(sessionId) : undefined}\n className=\"str-video__participant-details__name--pinned\"\n />\n )}\n {indicatorsVisible && <SpeechIndicator />}\n </div>\n </div>\n {indicatorsVisible && (\n <Notification\n isVisible={\n isLocalParticipant &&\n connectionQuality === SfuModels.ConnectionQuality.POOR\n }\n message={t('Poor connection quality')}\n >\n {connectionQualityAsString && (\n <span\n className={clsx(\n 'str-video__participant-details__connection-quality',\n `str-video__participant-details__connection-quality--${connectionQualityAsString}`,\n )}\n title={connectionQualityAsString}\n />\n )}\n </Notification>\n )}\n </>\n );\n};\n\nexport const SpeechIndicator = () => {\n const { participant } = useParticipantViewContext();\n const { isSpeaking, isDominantSpeaker } = participant;\n return (\n <span\n className={clsx(\n 'str-video__speech-indicator',\n isSpeaking && 'str-video__speech-indicator--speaking',\n isDominantSpeaker && 'str-video__speech-indicator--dominant',\n )}\n >\n <span className=\"str-video__speech-indicator__bar\" />\n <span className=\"str-video__speech-indicator__bar\" />\n <span className=\"str-video__speech-indicator__bar\" />\n </span>\n );\n};\n"],"names":["_jsx","_jsxs","ParticipantActionsContextMenu","DefaultParticipantActionsContextMenu","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultParticipantViewUI.js","sources":["../../../../../../../../src/core/components/ParticipantView/DefaultParticipantViewUI.tsx"],"sourcesContent":["import { ComponentType, forwardRef } from 'react';\nimport { Placement } from '@floating-ui/react';\nimport {\n hasAudio,\n hasPausedTrack,\n hasScreenShare,\n hasVideo,\n SfuModels,\n} from '@stream-io/video-client';\nimport {\n useCall,\n useCallStateHooks,\n useIsAudioConnecting,\n useIsVideoConnecting,\n} from '@stream-io/video-react-bindings';\nimport { useI18n } from '../../../i18n';\nimport clsx from 'clsx';\n\nimport {\n Button,\n Icon,\n IconButton,\n LoadingIndicator,\n MenuToggle,\n Notification,\n ToggleMenuButtonProps,\n} from '../../../components';\nimport { ParticipantActionsContextMenu as DefaultParticipantActionsContextMenu } from './ParticipantActionsContextMenu';\nimport { ConnectionQualityIndicator } from './ConnectionQualityIndicator';\nimport { Reaction } from '../../../components/Reaction';\nimport { useParticipantViewContext } from './ParticipantViewContext';\n\nexport type DefaultParticipantViewUIProps = {\n /**\n * Turns on/off the status indicator icons (mute, connection quality, etc...).\n */\n indicatorsVisible?: boolean;\n /**\n * Placement of the context menu component when opened\n */\n menuPlacement?: Placement;\n /**\n * Option to show/hide menu button component\n */\n showMenuButton?: boolean;\n /**\n * Custom component to render the context menu\n */\n ParticipantActionsContextMenu?: ComponentType;\n /**\n * Rendered over the tile when this participant's audio is blocked by the\n * browser's autoplay policy. Selecting it calls {@link Call.resumeAudio} to\n * unblock audio. Defaults to\n * {@link DefaultAudioBlockedNotification}; pass `null` to disable.\n */\n AudioBlockedNotification?: ComponentType | null;\n};\n\nconst ToggleButton = forwardRef<HTMLButtonElement, ToggleMenuButtonProps>(\n function ToggleButtonRender(props, ref) {\n const { t } = useI18n();\n\n return (\n <IconButton\n className=\"str-video__participant-view__menu-button\"\n active={props.menuShown}\n size=\"sm\"\n variant=\"secondary\"\n ref={ref}\n title={t('participantView.moreOptions.title', 'More options')}\n icon=\"ellipsis\"\n />\n );\n },\n);\n\nexport const DefaultScreenShareOverlay = () => {\n const call = useCall();\n const { t } = useI18n();\n\n const stopScreenShare = () => {\n call?.screenShare.disable().catch((err) => {\n console.error('Failed to stop screen sharing:', err);\n });\n };\n\n return (\n <div className=\"str-video__screen-share-overlay\">\n <Icon icon=\"screen-share-off\" />\n <span className=\"str-video__screen-share-overlay__title\">\n {t(\n 'common.presentingYourScreen.text',\n 'You are presenting your screen',\n )}\n </span>\n <Button\n variant=\"destructive\"\n onClick={stopScreenShare}\n className=\"str-video__screen-share-overlay__button\"\n >\n <Icon icon=\"close\" />{' '}\n {t('participantView.stopScreenSharing.label', 'Stop Screen Sharing')}\n </Button>\n </div>\n );\n};\n\n/**\n * Rendered over a remote participant's tile when the browser's autoplay policy\n * has blocked their audio. Selecting it calls `call.resumeAudio()`, unblocking\n * every blocked element at once.\n */\nexport const DefaultAudioBlockedNotification = () => {\n const call = useCall();\n const { t } = useI18n();\n\n return (\n <button\n type=\"button\"\n className=\"str-video__audio-blocked-notification\"\n onClick={call?.resumeAudio}\n aria-live=\"polite\"\n >\n <span className=\"str-video__audio-blocked-notification__message\">\n <Icon icon=\"speaker\" />\n <span className=\"str-video__audio-blocked-notification__text\">\n <span className=\"str-video__audio-blocked-notification__title\">\n {t(\n 'participantView.audioBlocked.title',\n 'Audio is blocked by your system',\n )}\n </span>\n <span className=\"str-video__audio-blocked-notification__subtitle\">\n {t(\n 'participantView.audioBlocked.description',\n 'Click or tap to play audio',\n )}\n </span>\n </span>\n </span>\n </button>\n );\n};\n\nexport const DefaultParticipantViewUI = ({\n indicatorsVisible = true,\n menuPlacement = 'bottom-start',\n showMenuButton = true,\n ParticipantActionsContextMenu = DefaultParticipantActionsContextMenu,\n AudioBlockedNotification = DefaultAudioBlockedNotification,\n}: DefaultParticipantViewUIProps) => {\n const { participant, trackType } = useParticipantViewContext();\n const { useAutoplayBlockedSessionIds } = useCallStateHooks();\n const blockedSessionIds = useAutoplayBlockedSessionIds();\n const isScreenSharing = hasScreenShare(participant);\n const shouldShowAudioBlockedNotification =\n !participant.isLocalParticipant &&\n trackType !== 'screenShareTrack' &&\n blockedSessionIds.includes(participant.sessionId);\n\n if (\n participant.isLocalParticipant &&\n isScreenSharing &&\n trackType === 'screenShareTrack'\n ) {\n return (\n <>\n <DefaultScreenShareOverlay />\n <ParticipantDetails indicatorsVisible={indicatorsVisible} />\n </>\n );\n }\n\n return (\n <>\n {showMenuButton && (\n <MenuToggle\n strategy=\"fixed\"\n placement={menuPlacement}\n ToggleButton={ToggleButton}\n >\n <ParticipantActionsContextMenu />\n </MenuToggle>\n )}\n <Reaction participant={participant} />\n <ParticipantDetails indicatorsVisible={indicatorsVisible} />\n {AudioBlockedNotification && shouldShowAudioBlockedNotification && (\n <AudioBlockedNotification />\n )}\n </>\n );\n};\n\nexport const ParticipantDetails = ({\n indicatorsVisible = true,\n}: Pick<DefaultParticipantViewUIProps, 'indicatorsVisible'>) => {\n const { participant, trackType } = useParticipantViewContext();\n const {\n isLocalParticipant,\n connectionQuality,\n pin,\n sessionId,\n name,\n userId,\n } = participant;\n const call = useCall();\n\n const { t } = useI18n();\n const hasAudioTrack = hasAudio(participant);\n const hasVideoTrack = hasVideo(participant);\n const canUnpin = !!pin && pin.isLocalPin;\n const isTrackPaused =\n trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;\n\n const isAudioConnecting = useIsAudioConnecting(participant);\n const isVideoConnecting = useIsVideoConnecting(participant);\n\n return (\n <>\n <div className=\"str-video__participant-details\">\n <div className=\"str-video__participant-details__name\">\n <span className=\"str-video__participant-details__name-text\">\n {name || userId}\n </span>\n {indicatorsVisible && isAudioConnecting && (\n <LoadingIndicator\n className=\"str-video__participant-details__name--audio-connecting\"\n tooltip={t(\n 'participantView.audioConnecting.title',\n 'Audio is connecting...',\n )}\n />\n )}\n {indicatorsVisible && !hasAudioTrack && (\n <span className=\"str-video__participant-details__name--audio-muted\" />\n )}\n {indicatorsVisible && isVideoConnecting && (\n <LoadingIndicator\n className=\"str-video__participant-details__name--video-connecting\"\n tooltip={t(\n 'participantView.videoConnecting.title',\n 'Video is connecting...',\n )}\n />\n )}\n {indicatorsVisible && !hasVideoTrack && (\n <span className=\"str-video__participant-details__name--video-muted\" />\n )}\n {indicatorsVisible && isTrackPaused && (\n <span\n title={t(\n 'participantView.videoPausedInsufficientBandwidth.title',\n 'Video paused due to insufficient bandwidth',\n )}\n className=\"str-video__participant-details__name--track-paused\"\n />\n )}\n {indicatorsVisible && pin && (\n <span\n title={\n canUnpin\n ? t('participantView.unpin.label', 'Unpin')\n : t('participantList.pinned.title', 'Pinned')\n }\n onClick={canUnpin ? () => call?.unpin(sessionId) : undefined}\n className=\"str-video__participant-details__name--pinned\"\n />\n )}\n {indicatorsVisible && <SpeechIndicator />}\n </div>\n </div>\n {indicatorsVisible && (\n <Notification\n state=\"error\"\n isVisible={\n isLocalParticipant &&\n connectionQuality === SfuModels.ConnectionQuality.POOR\n }\n message={t(\n 'participantView.connectionQuality.poor.text',\n 'Poor connection quality. Please check your internet connection.',\n )}\n >\n {connectionQuality && (\n <ConnectionQualityIndicator quality={connectionQuality} />\n )}\n </Notification>\n )}\n </>\n );\n};\n\nexport const SpeechIndicator = () => {\n const { participant } = useParticipantViewContext();\n const { isSpeaking, isDominantSpeaker } = participant;\n return (\n <span\n className={clsx(\n 'str-video__speech-indicator',\n isSpeaking && 'str-video__speech-indicator--speaking',\n isDominantSpeaker && 'str-video__speech-indicator--dominant',\n )}\n >\n <span className=\"str-video__speech-indicator__bar\" />\n <span className=\"str-video__speech-indicator__bar\" />\n <span className=\"str-video__speech-indicator__bar\" />\n </span>\n );\n};\n"],"names":["_jsx","_jsxs","ParticipantActionsContextMenu","DefaultParticipantActionsContextMenu","_Fragment"],"mappings":"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"}
|