@stream-io/video-react-sdk 1.41.0 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/CHANGELOG.md +22 -6
  2. package/dist/embedded.cjs.js +1 -1
  3. package/dist/esm/embedded.js +3 -0
  4. package/dist/esm/embedded.js.map +1 -0
  5. package/dist/esm/index.js +92 -0
  6. package/dist/esm/index.js.map +1 -0
  7. package/dist/esm/src/components/Avatar/Avatar.js +14 -0
  8. package/dist/esm/src/components/Avatar/Avatar.js.map +1 -0
  9. package/dist/{background-filters-D2U5UBbH.es.js → esm/src/components/BackgroundFilters/BackgroundFilters.js} +1 -1
  10. package/dist/esm/src/components/BackgroundFilters/BackgroundFilters.js.map +1 -0
  11. package/dist/esm/src/components/BackgroundFilters/BackgroundFiltersProvider.js +33 -0
  12. package/dist/esm/src/components/BackgroundFilters/BackgroundFiltersProvider.js.map +1 -0
  13. package/dist/esm/src/components/Button/CompositeButton.js +29 -0
  14. package/dist/esm/src/components/Button/CompositeButton.js.map +1 -0
  15. package/dist/esm/src/components/Button/IconButton.js +18 -0
  16. package/dist/esm/src/components/Button/IconButton.js.map +1 -0
  17. package/dist/esm/src/components/Button/TextButton.js +8 -0
  18. package/dist/esm/src/components/Button/TextButton.js.map +1 -0
  19. package/dist/esm/src/components/CallControls/AcceptCallButton.js +28 -0
  20. package/dist/esm/src/components/CallControls/AcceptCallButton.js.map +1 -0
  21. package/dist/esm/src/components/CallControls/CallControls.js +21 -0
  22. package/dist/esm/src/components/CallControls/CallControls.js.map +1 -0
  23. package/dist/esm/src/components/CallControls/CallStatsButton.js +18 -0
  24. package/dist/esm/src/components/CallControls/CallStatsButton.js.map +1 -0
  25. package/dist/esm/src/components/CallControls/CancelCallButton.js +78 -0
  26. package/dist/esm/src/components/CallControls/CancelCallButton.js.map +1 -0
  27. package/dist/esm/src/components/CallControls/ReactionsButton.js +61 -0
  28. package/dist/esm/src/components/CallControls/ReactionsButton.js.map +1 -0
  29. package/dist/esm/src/components/CallControls/RecordCallButton.js +64 -0
  30. package/dist/esm/src/components/CallControls/RecordCallButton.js.map +1 -0
  31. package/dist/esm/src/components/CallControls/ScreenShareButton.js +44 -0
  32. package/dist/esm/src/components/CallControls/ScreenShareButton.js.map +1 -0
  33. package/dist/esm/src/components/CallControls/ToggleAudioButton.js +72 -0
  34. package/dist/esm/src/components/CallControls/ToggleAudioButton.js.map +1 -0
  35. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js +27 -0
  36. package/dist/esm/src/components/CallControls/ToggleAudioOutputButton.js.map +1 -0
  37. package/dist/esm/src/components/CallControls/ToggleVideoButton.js +81 -0
  38. package/dist/esm/src/components/CallControls/ToggleVideoButton.js.map +1 -0
  39. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js +23 -0
  40. package/dist/esm/src/components/CallParticipantsList/BlockedUserListing.js.map +1 -0
  41. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js +15 -0
  42. package/dist/esm/src/components/CallParticipantsList/CallParticipantListHeader.js.map +1 -0
  43. package/dist/esm/src/components/CallParticipantsList/CallParticipantListing.js +7 -0
  44. package/dist/esm/src/components/CallParticipantsList/CallParticipantListing.js.map +1 -0
  45. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js +46 -0
  46. package/dist/esm/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -0
  47. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js +81 -0
  48. package/dist/esm/src/components/CallParticipantsList/CallParticipantsList.js.map +1 -0
  49. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js +10 -0
  50. package/dist/esm/src/components/CallParticipantsList/EmptyParticipantSearchList.js.map +1 -0
  51. package/dist/esm/src/components/CallPreview/CallPreview.js +24 -0
  52. package/dist/esm/src/components/CallPreview/CallPreview.js.map +1 -0
  53. package/dist/esm/src/components/CallRecordingList/CallRecordingList.js +12 -0
  54. package/dist/esm/src/components/CallRecordingList/CallRecordingList.js.map +1 -0
  55. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js +12 -0
  56. package/dist/esm/src/components/CallRecordingList/CallRecordingListHeader.js.map +1 -0
  57. package/dist/esm/src/components/CallRecordingList/CallRecordingListItem.js +14 -0
  58. package/dist/esm/src/components/CallRecordingList/CallRecordingListItem.js.map +1 -0
  59. package/dist/esm/src/components/CallRecordingList/EmptyCallRecordingListing.js +8 -0
  60. package/dist/esm/src/components/CallRecordingList/EmptyCallRecordingListing.js.map +1 -0
  61. package/dist/esm/src/components/CallRecordingList/LoadingCallRecordingListing.js +10 -0
  62. package/dist/esm/src/components/CallRecordingList/LoadingCallRecordingListing.js.map +1 -0
  63. package/dist/esm/src/components/CallStats/CallStats.js +180 -0
  64. package/dist/esm/src/components/CallStats/CallStats.js.map +1 -0
  65. package/dist/{latency-chart-Bj5OSYzg.es.js → esm/src/components/CallStats/CallStatsLatencyChart.js} +1 -1
  66. package/dist/esm/src/components/CallStats/CallStatsLatencyChart.js.map +1 -0
  67. package/dist/esm/src/components/DeviceSettings/AudioVolumeIndicator.js +27 -0
  68. package/dist/esm/src/components/DeviceSettings/AudioVolumeIndicator.js.map +1 -0
  69. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js +42 -0
  70. package/dist/esm/src/components/DeviceSettings/DeviceAudioPreviewItem.js.map +1 -0
  71. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js +67 -0
  72. package/dist/esm/src/components/DeviceSettings/DeviceSelector.js.map +1 -0
  73. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js +26 -0
  74. package/dist/esm/src/components/DeviceSettings/DeviceSelectorAudio.js.map +1 -0
  75. package/dist/esm/src/components/DeviceSettings/DeviceSelectorVideo.js +15 -0
  76. package/dist/esm/src/components/DeviceSettings/DeviceSelectorVideo.js.map +1 -0
  77. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js +26 -0
  78. package/dist/esm/src/components/DeviceSettings/DeviceSettings.js.map +1 -0
  79. package/dist/esm/src/components/DeviceSettings/DeviceVideoPreviewItem.js +42 -0
  80. package/dist/esm/src/components/DeviceSettings/DeviceVideoPreviewItem.js.map +1 -0
  81. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js +57 -0
  82. package/dist/esm/src/components/DeviceSettings/SpeakerTest.js.map +1 -0
  83. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js +79 -0
  84. package/dist/esm/src/components/DropdownSelect/DropdownSelect.js.map +1 -0
  85. package/dist/esm/src/components/Icon/Icon.js +7 -0
  86. package/dist/esm/src/components/Icon/Icon.js.map +1 -0
  87. package/dist/esm/src/components/LoadingIndicator/LoadingIndicator.js +9 -0
  88. package/dist/esm/src/components/LoadingIndicator/LoadingIndicator.js.map +1 -0
  89. package/dist/esm/src/components/Menu/GenericMenu.js +19 -0
  90. package/dist/esm/src/components/Menu/GenericMenu.js.map +1 -0
  91. package/dist/esm/src/components/Menu/MenuToggle.js +75 -0
  92. package/dist/esm/src/components/Menu/MenuToggle.js.map +1 -0
  93. package/dist/esm/src/components/NoiseCancellation/NoiseCancellationProvider.js +96 -0
  94. package/dist/esm/src/components/NoiseCancellation/NoiseCancellationProvider.js.map +1 -0
  95. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js +23 -0
  96. package/dist/esm/src/components/Notification/MicCaptureErrorNotification.js.map +1 -0
  97. package/dist/esm/src/components/Notification/Notification.js +31 -0
  98. package/dist/esm/src/components/Notification/Notification.js.map +1 -0
  99. package/dist/esm/src/components/Notification/PermissionNotification.js +32 -0
  100. package/dist/esm/src/components/Notification/PermissionNotification.js.map +1 -0
  101. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js +23 -0
  102. package/dist/esm/src/components/Notification/RecordingInProgressNotification.js.map +1 -0
  103. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js +14 -0
  104. package/dist/esm/src/components/Notification/SpeakingWhileMutedNotification.js.map +1 -0
  105. package/dist/esm/src/components/Permissions/PermissionRequests.js +99 -0
  106. package/dist/esm/src/components/Permissions/PermissionRequests.js.map +1 -0
  107. package/dist/esm/src/components/Reaction/Reaction.js +32 -0
  108. package/dist/esm/src/components/Reaction/Reaction.js.map +1 -0
  109. package/dist/esm/src/components/RingingCall/RingingCall.js +48 -0
  110. package/dist/esm/src/components/RingingCall/RingingCall.js.map +1 -0
  111. package/dist/esm/src/components/RingingCall/RingingCallControls.js +34 -0
  112. package/dist/esm/src/components/RingingCall/RingingCallControls.js.map +1 -0
  113. package/dist/esm/src/components/Search/SearchInput.js +27 -0
  114. package/dist/esm/src/components/Search/SearchInput.js.map +1 -0
  115. package/dist/esm/src/components/Search/SearchResults.js +15 -0
  116. package/dist/esm/src/components/Search/SearchResults.js.map +1 -0
  117. package/dist/esm/src/components/Search/hooks/useSearch.js +38 -0
  118. package/dist/esm/src/components/Search/hooks/useSearch.js.map +1 -0
  119. package/dist/esm/src/components/StreamTheme/StreamTheme.js +9 -0
  120. package/dist/esm/src/components/StreamTheme/StreamTheme.js.map +1 -0
  121. package/dist/esm/src/components/Tooltip/Tooltip.js +29 -0
  122. package/dist/esm/src/components/Tooltip/Tooltip.js.map +1 -0
  123. package/dist/esm/src/components/Tooltip/WithTooltip.js +15 -0
  124. package/dist/esm/src/components/Tooltip/WithTooltip.js.map +1 -0
  125. package/dist/esm/src/components/Tooltip/hooks/useEnterLeaveHandlers.js +17 -0
  126. package/dist/esm/src/components/Tooltip/hooks/useEnterLeaveHandlers.js.map +1 -0
  127. package/dist/esm/src/components/VideoPreview/VideoPreview.js +37 -0
  128. package/dist/esm/src/components/VideoPreview/VideoPreview.js.map +1 -0
  129. package/dist/esm/src/core/components/Audio/Audio.js +22 -0
  130. package/dist/esm/src/core/components/Audio/Audio.js.map +1 -0
  131. package/dist/esm/src/core/components/Audio/ParticipantsAudio.js +23 -0
  132. package/dist/esm/src/core/components/Audio/ParticipantsAudio.js.map +1 -0
  133. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js +150 -0
  134. package/dist/esm/src/core/components/CallLayout/LivestreamLayout.js.map +1 -0
  135. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js +79 -0
  136. package/dist/esm/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -0
  137. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js +80 -0
  138. package/dist/esm/src/core/components/CallLayout/PipLayout/Grid.js.map +1 -0
  139. package/dist/esm/src/core/components/CallLayout/PipLayout/Host.js +13 -0
  140. package/dist/esm/src/core/components/CallLayout/PipLayout/Host.js.map +1 -0
  141. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js +57 -0
  142. package/dist/esm/src/core/components/CallLayout/PipLayout/Pip.js.map +1 -0
  143. package/dist/esm/src/core/components/CallLayout/PipLayout/index.js +8 -0
  144. package/dist/esm/src/core/components/CallLayout/PipLayout/index.js.map +1 -0
  145. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js +103 -0
  146. package/dist/esm/src/core/components/CallLayout/SpeakerLayout.js.map +1 -0
  147. package/dist/esm/src/core/components/CallLayout/hooks.js +62 -0
  148. package/dist/esm/src/core/components/CallLayout/hooks.js.map +1 -0
  149. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js +94 -0
  150. package/dist/esm/src/core/components/ParticipantView/DefaultParticipantViewUI.js.map +1 -0
  151. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js +97 -0
  152. package/dist/esm/src/core/components/ParticipantView/ParticipantActionsContextMenu.js.map +1 -0
  153. package/dist/esm/src/core/components/ParticipantView/ParticipantView.js +65 -0
  154. package/dist/esm/src/core/components/ParticipantView/ParticipantView.js.map +1 -0
  155. package/dist/esm/src/core/components/ParticipantView/ParticipantViewContext.js +7 -0
  156. package/dist/esm/src/core/components/ParticipantView/ParticipantViewContext.js.map +1 -0
  157. package/dist/esm/src/core/components/StreamCall/StreamCall.js +8 -0
  158. package/dist/esm/src/core/components/StreamCall/StreamCall.js.map +1 -0
  159. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js +11 -0
  160. package/dist/esm/src/core/components/StreamVideo/StreamVideo.js.map +1 -0
  161. package/dist/esm/src/core/components/Video/BaseVideo.js +41 -0
  162. package/dist/esm/src/core/components/Video/BaseVideo.js.map +1 -0
  163. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js +21 -0
  164. package/dist/esm/src/core/components/Video/BaseVideoPlaceholder.js.map +1 -0
  165. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js +12 -0
  166. package/dist/esm/src/core/components/Video/DefaultPictureInPicturePlaceholder.js.map +1 -0
  167. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js +12 -0
  168. package/dist/esm/src/core/components/Video/DefaultVideoPlaceholder.js.map +1 -0
  169. package/dist/esm/src/core/components/Video/Video.js +88 -0
  170. package/dist/esm/src/core/components/Video/Video.js.map +1 -0
  171. package/dist/esm/src/core/hooks/useCalculateHardLimit.js +59 -0
  172. package/dist/esm/src/core/hooks/useCalculateHardLimit.js.map +1 -0
  173. package/dist/esm/src/core/hooks/usePictureInPictureState.js +24 -0
  174. package/dist/esm/src/core/hooks/usePictureInPictureState.js.map +1 -0
  175. package/dist/esm/src/core/hooks/useTrackElementVisibility.js +14 -0
  176. package/dist/esm/src/core/hooks/useTrackElementVisibility.js.map +1 -0
  177. package/dist/esm/src/embedded/EmbeddedClientProvider.js +83 -0
  178. package/dist/esm/src/embedded/EmbeddedClientProvider.js.map +1 -0
  179. package/dist/esm/src/embedded/call/CallControls.js +65 -0
  180. package/dist/esm/src/embedded/call/CallControls.js.map +1 -0
  181. package/dist/esm/src/embedded/call/CallHeader.js +57 -0
  182. package/dist/esm/src/embedded/call/CallHeader.js.map +1 -0
  183. package/dist/esm/src/embedded/call/CallLayout.js +63 -0
  184. package/dist/esm/src/embedded/call/CallLayout.js.map +1 -0
  185. package/dist/esm/src/embedded/call/CallStateRouter.js +87 -0
  186. package/dist/esm/src/embedded/call/CallStateRouter.js.map +1 -0
  187. package/dist/esm/src/embedded/call/EmbeddedCall.js +12 -0
  188. package/dist/esm/src/embedded/call/EmbeddedCall.js.map +1 -0
  189. package/dist/esm/src/embedded/context/ConfigurationContext.js +21 -0
  190. package/dist/esm/src/embedded/context/ConfigurationContext.js.map +1 -0
  191. package/dist/esm/src/embedded/hooks/useCallDuration.js +32 -0
  192. package/dist/esm/src/embedded/hooks/useCallDuration.js.map +1 -0
  193. package/dist/esm/src/embedded/hooks/useEmbeddedClient.js +40 -0
  194. package/dist/esm/src/embedded/hooks/useEmbeddedClient.js.map +1 -0
  195. package/dist/esm/src/embedded/hooks/useInitializeCall.js +39 -0
  196. package/dist/esm/src/embedded/hooks/useInitializeCall.js.map +1 -0
  197. package/dist/esm/src/embedded/hooks/useInitializeVideoClient.js +93 -0
  198. package/dist/esm/src/embedded/hooks/useInitializeVideoClient.js.map +1 -0
  199. package/dist/esm/src/embedded/hooks/useIsLivestreamPaused.js +42 -0
  200. package/dist/esm/src/embedded/hooks/useIsLivestreamPaused.js.map +1 -0
  201. package/dist/esm/src/embedded/hooks/useLayout.js +122 -0
  202. package/dist/esm/src/embedded/hooks/useLayout.js.map +1 -0
  203. package/dist/esm/src/embedded/hooks/useNoiseCancellationLoader.js +48 -0
  204. package/dist/esm/src/embedded/hooks/useNoiseCancellationLoader.js.map +1 -0
  205. package/dist/esm/src/embedded/hooks/useWakeLock.js +34 -0
  206. package/dist/esm/src/embedded/hooks/useWakeLock.js.map +1 -0
  207. package/dist/esm/src/embedded/livestream/EmbeddedLivestream.js +11 -0
  208. package/dist/esm/src/embedded/livestream/EmbeddedLivestream.js.map +1 -0
  209. package/dist/esm/src/embedded/livestream/LivestreamUI.js +17 -0
  210. package/dist/esm/src/embedded/livestream/LivestreamUI.js.map +1 -0
  211. package/dist/esm/src/embedded/livestream/host/HostLayout.js +76 -0
  212. package/dist/esm/src/embedded/livestream/host/HostLayout.js.map +1 -0
  213. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js +110 -0
  214. package/dist/esm/src/embedded/livestream/host/HostStateRouter.js.map +1 -0
  215. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js +74 -0
  216. package/dist/esm/src/embedded/livestream/viewer/ViewerLayout.js.map +1 -0
  217. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js +109 -0
  218. package/dist/esm/src/embedded/livestream/viewer/ViewerLobby.js.map +1 -0
  219. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js +105 -0
  220. package/dist/esm/src/embedded/livestream/viewer/ViewerStateRouter.js.map +1 -0
  221. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js +65 -0
  222. package/dist/esm/src/embedded/shared/BlurToggleButton/BlurToggleButton.js.map +1 -0
  223. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js +34 -0
  224. package/dist/esm/src/embedded/shared/CallFeedback/CallEndedScreen.js.map +1 -0
  225. package/dist/esm/src/embedded/shared/CallFeedback/CallFeedback.js +35 -0
  226. package/dist/esm/src/embedded/shared/CallFeedback/CallFeedback.js.map +1 -0
  227. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js +18 -0
  228. package/dist/esm/src/embedded/shared/CallFeedback/RatingScreen.js.map +1 -0
  229. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js +37 -0
  230. package/dist/esm/src/embedded/shared/CallFeedback/StarRating.js.map +1 -0
  231. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js +34 -0
  232. package/dist/esm/src/embedded/shared/CallFeedback/ThankYouScreen.js.map +1 -0
  233. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js +56 -0
  234. package/dist/esm/src/embedded/shared/ConnectionNotification/ConnectionNotification.js.map +1 -0
  235. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js +42 -0
  236. package/dist/esm/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.js.map +1 -0
  237. package/dist/esm/src/embedded/shared/JoinError/JoinError.js +34 -0
  238. package/dist/esm/src/embedded/shared/JoinError/JoinError.js.map +1 -0
  239. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js +44 -0
  240. package/dist/esm/src/embedded/shared/Lobby/DeviceControls.js.map +1 -0
  241. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js +31 -0
  242. package/dist/esm/src/embedded/shared/Lobby/DisabledDeviceButton.js.map +1 -0
  243. package/dist/esm/src/embedded/shared/Lobby/Lobby.js +45 -0
  244. package/dist/esm/src/embedded/shared/Lobby/Lobby.js.map +1 -0
  245. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js +40 -0
  246. package/dist/esm/src/embedded/shared/Lobby/ToggleCameraButton.js.map +1 -0
  247. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js +41 -0
  248. package/dist/esm/src/embedded/shared/Lobby/ToggleMicButton.js.map +1 -0
  249. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js +50 -0
  250. package/dist/esm/src/embedded/shared/Lobby/VideoPreviewFallbacks.js.map +1 -0
  251. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js +31 -0
  252. package/dist/esm/src/embedded/shared/ViewersCount/ViewersCount.js.map +1 -0
  253. package/dist/esm/src/hooks/useDeviceList.js +40 -0
  254. package/dist/esm/src/hooks/useDeviceList.js.map +1 -0
  255. package/dist/esm/src/hooks/useDragToScroll.js +116 -0
  256. package/dist/esm/src/hooks/useDragToScroll.js.map +1 -0
  257. package/dist/esm/src/hooks/useFloatingUIPreset.js +34 -0
  258. package/dist/esm/src/hooks/useFloatingUIPreset.js.map +1 -0
  259. package/dist/esm/src/hooks/useLoopbackRecording.js +138 -0
  260. package/dist/esm/src/hooks/useLoopbackRecording.js.map +1 -0
  261. package/dist/esm/src/hooks/useModeration.js +135 -0
  262. package/dist/esm/src/hooks/useModeration.js.map +1 -0
  263. package/dist/esm/src/hooks/usePersistedDevicePreferences.js +16 -0
  264. package/dist/esm/src/hooks/usePersistedDevicePreferences.js.map +1 -0
  265. package/dist/esm/src/hooks/useRequestPermission.js +41 -0
  266. package/dist/esm/src/hooks/useRequestPermission.js.map +1 -0
  267. package/dist/esm/src/hooks/useScrollPosition.js +66 -0
  268. package/dist/esm/src/hooks/useScrollPosition.js.map +1 -0
  269. package/dist/esm/src/translations/en.json.js +177 -0
  270. package/dist/esm/src/translations/en.json.js.map +1 -0
  271. package/dist/esm/src/translations/index.js +6 -0
  272. package/dist/esm/src/translations/index.js.map +1 -0
  273. package/dist/esm/src/utilities/applyElementToRef.js +10 -0
  274. package/dist/esm/src/utilities/applyElementToRef.js.map +1 -0
  275. package/dist/esm/src/utilities/callControlHandler.js +29 -0
  276. package/dist/esm/src/utilities/callControlHandler.js.map +1 -0
  277. package/dist/esm/src/utilities/chunk.js +7 -0
  278. package/dist/esm/src/utilities/chunk.js.map +1 -0
  279. package/dist/esm/src/utilities/filter.js +94 -0
  280. package/dist/esm/src/utilities/filter.js.map +1 -0
  281. package/dist/esm/src/utilities/isComponentType.js +10 -0
  282. package/dist/esm/src/utilities/isComponentType.js.map +1 -0
  283. package/dist/esm/src/utilities/normalizeString.js +11 -0
  284. package/dist/esm/src/utilities/normalizeString.js.map +1 -0
  285. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js +105 -0
  286. package/dist/esm/src/wrappers/LivestreamPlayer/LivestreamPlayer.js.map +1 -0
  287. package/dist/index.cjs.js +135 -2
  288. package/dist/index.cjs.js.map +1 -1
  289. package/dist/index.d.ts +1 -1
  290. package/dist/src/hooks/index.d.ts +1 -0
  291. package/dist/src/hooks/useLoopbackRecording.d.ts +23 -0
  292. package/index.ts +4 -0
  293. package/package.json +14 -10
  294. package/src/hooks/index.ts +1 -0
  295. package/src/hooks/useLoopbackRecording.ts +207 -0
  296. package/dist/background-filters-D2U5UBbH.es.js.map +0 -1
  297. package/dist/embedded-BackgroundFilters-D2U5UBbH.es.js +0 -420
  298. package/dist/embedded-BackgroundFilters-D2U5UBbH.es.js.map +0 -1
  299. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js +0 -57
  300. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js.map +0 -1
  301. package/dist/embedded.es.js +0 -3536
  302. package/dist/embedded.es.js.map +0 -1
  303. package/dist/index.es.js +0 -3291
  304. package/dist/index.es.js.map +0 -1
  305. package/dist/latency-chart-Bj5OSYzg.es.js.map +0 -1
@@ -0,0 +1,50 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useI18n, useCallStateHooks, useConnectedUser } from '@stream-io/video-react-bindings';
3
+ import { Avatar } from '../../../components/Avatar/Avatar.js';
4
+ import '../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
5
+ import '../../../components/Button/CompositeButton.js';
6
+ import '../../../components/Button/IconButton.js';
7
+ import 'react';
8
+ import '@stream-io/video-client';
9
+ import 'clsx';
10
+ import '@floating-ui/react';
11
+ import '../../../components/CallControls/RecordCallButton.js';
12
+ import '../../../components/CallControls/ReactionsButton.js';
13
+ import '../../../components/DeviceSettings/DeviceSettings.js';
14
+ import '../../../components/DropdownSelect/DropdownSelect.js';
15
+ import '../../../components/Menu/MenuToggle.js';
16
+ import '../../../components/CallControls/CancelCallButton.js';
17
+ import '../../../components/CallControls/CallStatsButton.js';
18
+ import '../../../components/CallParticipantsList/CallParticipantsList.js';
19
+ import '../../../components/CallParticipantsList/CallParticipantListingItem.js';
20
+ import '../../../components/CallStats/CallStats.js';
21
+ import '../../../components/NoiseCancellation/NoiseCancellationProvider.js';
22
+ import '../../../components/RingingCall/RingingCall.js';
23
+ import '../../../components/Permissions/PermissionRequests.js';
24
+ import '../../../core/components/Video/Video.js';
25
+ import '../../../core/components/Video/BaseVideo.js';
26
+ import '../../../core/components/Video/DefaultVideoPlaceholder.js';
27
+
28
+ const DisabledVideoPreview = () => {
29
+ const { t } = useI18n();
30
+ const user = useConnectedUser();
31
+ const { useCameraState, useMicrophoneState } = useCallStateHooks();
32
+ const { hasBrowserPermission: hasCameraPermission } = useCameraState();
33
+ const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
34
+ const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
35
+ return (jsx("div", { className: "str-video__embedded-lobby__no-permission", children: hasBrowserMediaPermission ? (jsx(Avatar, { imageSrc: user?.image, name: user?.name || user?.id })) : (t('Please grant your browser permission to access your camera and microphone.')) }));
36
+ };
37
+ const NoCameraPreview = () => {
38
+ const { t } = useI18n();
39
+ const { useCameraState, useMicrophoneState } = useCallStateHooks();
40
+ const { hasBrowserPermission: hasCameraPermission } = useCameraState();
41
+ const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
42
+ const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
43
+ if (!hasBrowserMediaPermission) {
44
+ return (jsx("div", { className: "str-video__embedded-lobby__no-permission", children: t('Please grant your browser permission to access your camera and microphone.') }));
45
+ }
46
+ return (jsx("div", { className: "str-video__video-preview__no-camera-preview", children: t('No camera found') }));
47
+ };
48
+
49
+ export { DisabledVideoPreview, NoCameraPreview };
50
+ //# sourceMappingURL=VideoPreviewFallbacks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VideoPreviewFallbacks.js","sources":["../../../../../../../../src/embedded/shared/Lobby/VideoPreviewFallbacks.tsx"],"sourcesContent":["import {\n useCallStateHooks,\n useConnectedUser,\n useI18n,\n} from '@stream-io/video-react-bindings';\nimport { Avatar } from '../../../components';\n\nexport const DisabledVideoPreview = () => {\n const { t } = useI18n();\n\n const user = useConnectedUser();\n const { useCameraState, useMicrophoneState } = useCallStateHooks();\n\n const { hasBrowserPermission: hasCameraPermission } = useCameraState();\n const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();\n\n const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;\n\n return (\n <div className=\"str-video__embedded-lobby__no-permission\">\n {hasBrowserMediaPermission ? (\n <Avatar imageSrc={user?.image} name={user?.name || user?.id} />\n ) : (\n t(\n 'Please grant your browser permission to access your camera and microphone.',\n )\n )}\n </div>\n );\n};\n\nexport const NoCameraPreview = () => {\n const { t } = useI18n();\n const { useCameraState, useMicrophoneState } = useCallStateHooks();\n const { hasBrowserPermission: hasCameraPermission } = useCameraState();\n const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();\n\n const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;\n\n if (!hasBrowserMediaPermission) {\n return (\n <div className=\"str-video__embedded-lobby__no-permission\">\n {t(\n 'Please grant your browser permission to access your camera and microphone.',\n )}\n </div>\n );\n }\n\n return (\n <div className=\"str-video__video-preview__no-camera-preview\">\n {t('No camera found')}\n </div>\n );\n};\n"],"names":["_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,MAAM,oBAAoB,GAAG,MAAK;AACvC,IAAA,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE;AAEvB,IAAA,MAAM,IAAI,GAAG,gBAAgB,EAAE;IAC/B,MAAM,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,EAAE;IAElE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,GAAG,cAAc,EAAE;IACtE,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,GAAG,kBAAkB,EAAE;AAEvE,IAAA,MAAM,yBAAyB,GAAG,mBAAmB,IAAI,gBAAgB;AAEzE,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,0CAA0C,EAAA,QAAA,EACtD,yBAAyB,IACxBA,GAAA,CAAC,MAAM,EAAA,EAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,EAAE,GAAI,KAE/D,CAAC,CACC,4EAA4E,CAC7E,CACF,EAAA,CACG;AAEV;AAEO,MAAM,eAAe,GAAG,MAAK;AAClC,IAAA,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE;IACvB,MAAM,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,EAAE;IAClE,MAAM,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,GAAG,cAAc,EAAE;IACtE,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,GAAG,kBAAkB,EAAE;AAEvE,IAAA,MAAM,yBAAyB,GAAG,mBAAmB,IAAI,gBAAgB;IAEzE,IAAI,CAAC,yBAAyB,EAAE;AAC9B,QAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,0CAA0C,EAAA,QAAA,EACtD,CAAC,CACA,4EAA4E,CAC7E,EAAA,CACG;IAEV;AAEA,IAAA,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,6CAA6C,EAAA,QAAA,EACzD,CAAC,CAAC,iBAAiB,CAAC,EAAA,CACjB;AAEV;;;;"}
@@ -0,0 +1,31 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { humanize } from '@stream-io/video-client';
3
+ import 'react';
4
+ import 'clsx';
5
+ import '../../../components/BackgroundFilters/BackgroundFiltersProvider.js';
6
+ import '../../../components/Button/CompositeButton.js';
7
+ import '../../../components/Button/IconButton.js';
8
+ import '@stream-io/video-react-bindings';
9
+ import '@floating-ui/react';
10
+ import '../../../components/CallControls/RecordCallButton.js';
11
+ import '../../../components/CallControls/ReactionsButton.js';
12
+ import { Icon } from '../../../components/Icon/Icon.js';
13
+ import '../../../components/DeviceSettings/DeviceSettings.js';
14
+ import '../../../components/DropdownSelect/DropdownSelect.js';
15
+ import '../../../components/Menu/MenuToggle.js';
16
+ import '../../../components/CallControls/CancelCallButton.js';
17
+ import '../../../components/CallControls/CallStatsButton.js';
18
+ import '../../../components/CallParticipantsList/CallParticipantsList.js';
19
+ import '../../../components/CallParticipantsList/CallParticipantListingItem.js';
20
+ import '../../../components/CallStats/CallStats.js';
21
+ import '../../../components/NoiseCancellation/NoiseCancellationProvider.js';
22
+ import '../../../components/RingingCall/RingingCall.js';
23
+ import '../../../components/Permissions/PermissionRequests.js';
24
+ import '../../../core/components/Video/Video.js';
25
+ import '../../../core/components/Video/BaseVideo.js';
26
+ import '../../../core/components/Video/DefaultVideoPlaceholder.js';
27
+
28
+ const ViewersCount = ({ count }) => (jsxs("div", { className: "str-video__embedded-livestream-duration__viewers", children: [jsx(Icon, { icon: "eye", className: "str-video__embedded-livestream-duration__eye-icon" }), jsx("span", { className: "str-video__embedded-livestream-duration__count", children: humanize(count) })] }));
29
+
30
+ export { ViewersCount };
31
+ //# sourceMappingURL=ViewersCount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewersCount.js","sources":["../../../../../../../../src/embedded/shared/ViewersCount/ViewersCount.tsx"],"sourcesContent":["import { humanize } from '@stream-io/video-client';\nimport { Icon } from '../../../components';\n\ninterface ViewersCountProps {\n count: number;\n}\n\nexport const ViewersCount = ({ count }: ViewersCountProps) => (\n <div className=\"str-video__embedded-livestream-duration__viewers\">\n <Icon\n icon=\"eye\"\n className=\"str-video__embedded-livestream-duration__eye-icon\"\n />\n <span className=\"str-video__embedded-livestream-duration__count\">\n {humanize(count)}\n </span>\n </div>\n);\n"],"names":["_jsxs","_jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,MAAM,YAAY,GAAG,CAAC,EAAE,KAAK,EAAqB,MACvDA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,kDAAkD,EAAA,QAAA,EAAA,CAC/DC,IAAC,IAAI,EAAA,EACH,IAAI,EAAC,KAAK,EACV,SAAS,EAAC,mDAAmD,EAAA,CAC7D,EACFA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAC,gDAAgD,EAAA,QAAA,EAC7D,QAAQ,CAAC,KAAK,CAAC,EAAA,CACX,CAAA,EAAA,CACH;;;;"}
@@ -0,0 +1,40 @@
1
+ import { useI18n } from '@stream-io/video-react-bindings';
2
+ import { useMemo } from 'react';
3
+
4
+ /**
5
+ * Utility hook that helps render a list of devices or implement a device selector.
6
+ * Compared to someting like `useCameraState().devices`, it has some handy features:
7
+ * 1. Adds the "Default" device to the list if applicable (either the user did not
8
+ * select a device, or a previously selected device is no longer available).
9
+ * 2. Maps the device list to a format more suitable for rendering.
10
+ */
11
+ function useDeviceList(devices, selectedDeviceId) {
12
+ const { t } = useI18n();
13
+ return useMemo(() => {
14
+ let selectedDeviceInfo = null;
15
+ let selectedIndex = null;
16
+ const deviceList = devices.map((d, i) => {
17
+ const isSelected = d.deviceId === selectedDeviceId;
18
+ const device = { deviceId: d.deviceId, label: d.label, isSelected };
19
+ if (isSelected) {
20
+ selectedDeviceInfo = device;
21
+ selectedIndex = i;
22
+ }
23
+ return device;
24
+ });
25
+ if (selectedDeviceInfo === null || selectedIndex === null) {
26
+ const defaultDevice = {
27
+ deviceId: 'default',
28
+ label: t('Default'),
29
+ isSelected: true,
30
+ };
31
+ selectedDeviceInfo = defaultDevice;
32
+ selectedIndex = 0;
33
+ deviceList.unshift(defaultDevice);
34
+ }
35
+ return { deviceList, selectedDeviceInfo, selectedIndex };
36
+ }, [devices, selectedDeviceId, t]);
37
+ }
38
+
39
+ export { useDeviceList };
40
+ //# sourceMappingURL=useDeviceList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDeviceList.js","sources":["../../../../../../src/hooks/useDeviceList.tsx"],"sourcesContent":["import { useI18n } from '@stream-io/video-react-bindings';\nimport { useMemo } from 'react';\n\nexport interface DeviceListItem {\n deviceId: string;\n label: string;\n isSelected: boolean;\n}\n\n/**\n * Utility hook that helps render a list of devices or implement a device selector.\n * Compared to someting like `useCameraState().devices`, it has some handy features:\n * 1. Adds the \"Default\" device to the list if applicable (either the user did not\n * select a device, or a previously selected device is no longer available).\n * 2. Maps the device list to a format more suitable for rendering.\n */\nexport function useDeviceList(\n devices: MediaDeviceInfo[],\n selectedDeviceId: string | undefined,\n): {\n deviceList: DeviceListItem[];\n selectedDeviceInfo: DeviceListItem;\n selectedIndex: number;\n} {\n const { t } = useI18n();\n\n return useMemo(() => {\n let selectedDeviceInfo: DeviceListItem | null = null;\n let selectedIndex: number | null = null;\n\n const deviceList: DeviceListItem[] = devices.map((d, i) => {\n const isSelected = d.deviceId === selectedDeviceId;\n const device = { deviceId: d.deviceId, label: d.label, isSelected };\n\n if (isSelected) {\n selectedDeviceInfo = device;\n selectedIndex = i;\n }\n\n return device;\n });\n\n if (selectedDeviceInfo === null || selectedIndex === null) {\n const defaultDevice = {\n deviceId: 'default',\n label: t('Default'),\n isSelected: true,\n };\n\n selectedDeviceInfo = defaultDevice;\n selectedIndex = 0;\n deviceList.unshift(defaultDevice);\n }\n\n return { deviceList, selectedDeviceInfo, selectedIndex };\n }, [devices, selectedDeviceId, t]);\n}\n"],"names":[],"mappings":";;;AASA;;;;;;AAMG;AACG,SAAU,aAAa,CAC3B,OAA0B,EAC1B,gBAAoC,EAAA;AAMpC,IAAA,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE;IAEvB,OAAO,OAAO,CAAC,MAAK;QAClB,IAAI,kBAAkB,GAA0B,IAAI;QACpD,IAAI,aAAa,GAAkB,IAAI;QAEvC,MAAM,UAAU,GAAqB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAI;AACxD,YAAA,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,KAAK,gBAAgB;AAClD,YAAA,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE;YAEnE,IAAI,UAAU,EAAE;gBACd,kBAAkB,GAAG,MAAM;gBAC3B,aAAa,GAAG,CAAC;YACnB;AAEA,YAAA,OAAO,MAAM;AACf,QAAA,CAAC,CAAC;QAEF,IAAI,kBAAkB,KAAK,IAAI,IAAI,aAAa,KAAK,IAAI,EAAE;AACzD,YAAA,MAAM,aAAa,GAAG;AACpB,gBAAA,QAAQ,EAAE,SAAS;AACnB,gBAAA,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC;AACnB,gBAAA,UAAU,EAAE,IAAI;aACjB;YAED,kBAAkB,GAAG,aAAa;YAClC,aAAa,GAAG,CAAC;AACjB,YAAA,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC;QACnC;AAEA,QAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE;IAC1D,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC;AACpC;;;;"}
@@ -0,0 +1,116 @@
1
+ import { useRef, useEffect } from 'react';
2
+
3
+ /**
4
+ * Enables drag-to-scroll functionality with momentum scrolling on a scrollable element.
5
+ *
6
+ * This hook allows users to click and drag to scroll an element, with momentum scrolling
7
+ * that continues after the drag ends. The drag only activates after moving beyond a threshold
8
+ * distance, which prevents accidental drags from clicks.
9
+ *
10
+ * @param element - The HTML element to enable drag to scroll on.
11
+ * @param options - Options for customizing the drag-to-scroll behavior.
12
+ */
13
+ function useDragToScroll(element, options = {}) {
14
+ const { enabled, decay = 0.95, minVelocity = 0.5, dragThreshold = 5, } = options;
15
+ const stateRef = useRef({
16
+ isDragging: false,
17
+ isPointerActive: false,
18
+ prevX: 0,
19
+ prevY: 0,
20
+ velocityX: 0,
21
+ velocityY: 0,
22
+ rafId: 0,
23
+ startX: 0,
24
+ startY: 0,
25
+ });
26
+ useEffect(() => {
27
+ if (!element || !enabled)
28
+ return;
29
+ const state = stateRef.current;
30
+ const stopMomentum = () => {
31
+ if (state.rafId) {
32
+ cancelAnimationFrame(state.rafId);
33
+ state.rafId = 0;
34
+ }
35
+ state.velocityX = 0;
36
+ state.velocityY = 0;
37
+ };
38
+ const momentumStep = () => {
39
+ state.velocityX *= decay;
40
+ state.velocityY *= decay;
41
+ element.scrollLeft -= state.velocityX;
42
+ element.scrollTop -= state.velocityY;
43
+ if (Math.abs(state.velocityX) < minVelocity &&
44
+ Math.abs(state.velocityY) < minVelocity) {
45
+ state.rafId = 0;
46
+ return;
47
+ }
48
+ state.rafId = requestAnimationFrame(momentumStep);
49
+ };
50
+ const onPointerDown = (e) => {
51
+ if (e.pointerType !== 'mouse')
52
+ return;
53
+ stopMomentum();
54
+ state.isDragging = false;
55
+ state.isPointerActive = true;
56
+ state.prevX = e.clientX;
57
+ state.prevY = e.clientY;
58
+ state.startX = e.clientX;
59
+ state.startY = e.clientY;
60
+ };
61
+ const onPointerMove = (e) => {
62
+ if (e.pointerType !== 'mouse')
63
+ return;
64
+ if (!state.isPointerActive)
65
+ return;
66
+ const dx = e.clientX - state.startX;
67
+ const dy = e.clientY - state.startY;
68
+ if (!state.isDragging && Math.hypot(dx, dy) > dragThreshold) {
69
+ state.isDragging = true;
70
+ e.preventDefault();
71
+ }
72
+ if (!state.isDragging)
73
+ return;
74
+ const moveDx = e.clientX - state.prevX;
75
+ const moveDy = e.clientY - state.prevY;
76
+ element.scrollLeft -= moveDx;
77
+ element.scrollTop -= moveDy;
78
+ state.velocityX = moveDx;
79
+ state.velocityY = moveDy;
80
+ state.prevX = e.clientX;
81
+ state.prevY = e.clientY;
82
+ };
83
+ const onPointerUpOrCancel = () => {
84
+ const wasDragging = state.isDragging;
85
+ state.isDragging = false;
86
+ state.isPointerActive = false;
87
+ state.prevX = 0;
88
+ state.prevY = 0;
89
+ state.startX = 0;
90
+ state.startY = 0;
91
+ if (!wasDragging) {
92
+ stopMomentum();
93
+ return;
94
+ }
95
+ if (Math.hypot(state.velocityX, state.velocityY) < minVelocity) {
96
+ stopMomentum();
97
+ return;
98
+ }
99
+ state.rafId = requestAnimationFrame(momentumStep);
100
+ };
101
+ element.addEventListener('pointerdown', onPointerDown);
102
+ element.addEventListener('pointermove', onPointerMove);
103
+ window.addEventListener('pointerup', onPointerUpOrCancel);
104
+ window.addEventListener('pointercancel', onPointerUpOrCancel);
105
+ return () => {
106
+ element.removeEventListener('pointerdown', onPointerDown);
107
+ element.removeEventListener('pointermove', onPointerMove);
108
+ window.removeEventListener('pointerup', onPointerUpOrCancel);
109
+ window.removeEventListener('pointercancel', onPointerUpOrCancel);
110
+ stopMomentum();
111
+ };
112
+ }, [element, enabled, decay, minVelocity, dragThreshold]);
113
+ }
114
+
115
+ export { useDragToScroll };
116
+ //# sourceMappingURL=useDragToScroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDragToScroll.js","sources":["../../../../../../src/hooks/useDragToScroll.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\ninterface DragScrollState {\n isDragging: boolean;\n isPointerActive: boolean;\n prevX: number;\n prevY: number;\n velocityX: number;\n velocityY: number;\n rafId: number;\n startX: number;\n startY: number;\n}\n\ninterface DragToScrollOptions {\n decay?: number;\n minVelocity?: number;\n dragThreshold?: number;\n enabled?: boolean;\n}\n\n/**\n * Enables drag-to-scroll functionality with momentum scrolling on a scrollable element.\n *\n * This hook allows users to click and drag to scroll an element, with momentum scrolling\n * that continues after the drag ends. The drag only activates after moving beyond a threshold\n * distance, which prevents accidental drags from clicks.\n *\n * @param element - The HTML element to enable drag to scroll on.\n * @param options - Options for customizing the drag-to-scroll behavior.\n */\nexport function useDragToScroll(\n element: HTMLElement | null,\n options: DragToScrollOptions = {},\n) {\n const {\n enabled,\n decay = 0.95,\n minVelocity = 0.5,\n dragThreshold = 5,\n } = options;\n\n const stateRef = useRef<DragScrollState>({\n isDragging: false,\n isPointerActive: false,\n prevX: 0,\n prevY: 0,\n velocityX: 0,\n velocityY: 0,\n rafId: 0,\n startX: 0,\n startY: 0,\n });\n\n useEffect(() => {\n if (!element || !enabled) return;\n\n const state = stateRef.current;\n\n const stopMomentum = () => {\n if (state.rafId) {\n cancelAnimationFrame(state.rafId);\n state.rafId = 0;\n }\n state.velocityX = 0;\n state.velocityY = 0;\n };\n\n const momentumStep = () => {\n state.velocityX *= decay;\n state.velocityY *= decay;\n\n element.scrollLeft -= state.velocityX;\n element.scrollTop -= state.velocityY;\n\n if (\n Math.abs(state.velocityX) < minVelocity &&\n Math.abs(state.velocityY) < minVelocity\n ) {\n state.rafId = 0;\n return;\n }\n\n state.rafId = requestAnimationFrame(momentumStep);\n };\n\n const onPointerDown = (e: PointerEvent) => {\n if (e.pointerType !== 'mouse') return;\n\n stopMomentum();\n\n state.isDragging = false;\n state.isPointerActive = true;\n\n state.prevX = e.clientX;\n state.prevY = e.clientY;\n state.startX = e.clientX;\n state.startY = e.clientY;\n };\n\n const onPointerMove = (e: PointerEvent) => {\n if (e.pointerType !== 'mouse') return;\n\n if (!state.isPointerActive) return;\n\n const dx = e.clientX - state.startX;\n const dy = e.clientY - state.startY;\n\n if (!state.isDragging && Math.hypot(dx, dy) > dragThreshold) {\n state.isDragging = true;\n e.preventDefault();\n }\n\n if (!state.isDragging) return;\n\n const moveDx = e.clientX - state.prevX;\n const moveDy = e.clientY - state.prevY;\n\n element.scrollLeft -= moveDx;\n element.scrollTop -= moveDy;\n\n state.velocityX = moveDx;\n state.velocityY = moveDy;\n\n state.prevX = e.clientX;\n state.prevY = e.clientY;\n };\n\n const onPointerUpOrCancel = () => {\n const wasDragging = state.isDragging;\n\n state.isDragging = false;\n state.isPointerActive = false;\n\n state.prevX = 0;\n state.prevY = 0;\n state.startX = 0;\n state.startY = 0;\n\n if (!wasDragging) {\n stopMomentum();\n return;\n }\n\n if (Math.hypot(state.velocityX, state.velocityY) < minVelocity) {\n stopMomentum();\n return;\n }\n\n state.rafId = requestAnimationFrame(momentumStep);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n element.addEventListener('pointermove', onPointerMove);\n window.addEventListener('pointerup', onPointerUpOrCancel);\n window.addEventListener('pointercancel', onPointerUpOrCancel);\n\n return () => {\n element.removeEventListener('pointerdown', onPointerDown);\n element.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', onPointerUpOrCancel);\n window.removeEventListener('pointercancel', onPointerUpOrCancel);\n\n stopMomentum();\n };\n }, [element, enabled, decay, minVelocity, dragThreshold]);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { useEffect } from 'react';
2
+ import { useFloating, offset, shift, flip, size, autoUpdate } from '@floating-ui/react';
3
+
4
+ const useFloatingUIPreset = ({ middleware = [], placement, strategy, offset: offsetInPx = 10, }) => {
5
+ const { refs, x, y, update, elements: { domReference, floating }, context, } = useFloating({
6
+ placement,
7
+ strategy,
8
+ middleware: [
9
+ offset(offsetInPx),
10
+ shift(),
11
+ flip(),
12
+ size({
13
+ padding: 10,
14
+ apply: ({ availableHeight, elements }) => {
15
+ Object.assign(elements.floating.style, {
16
+ maxHeight: `${availableHeight}px`,
17
+ });
18
+ },
19
+ }),
20
+ ...middleware,
21
+ ],
22
+ });
23
+ // handle window resizing
24
+ useEffect(() => {
25
+ if (!domReference || !floating)
26
+ return;
27
+ const cleanup = autoUpdate(domReference, floating, update);
28
+ return () => cleanup();
29
+ }, [domReference, floating, update]);
30
+ return { refs, x, y, domReference, floating, strategy, context };
31
+ };
32
+
33
+ export { useFloatingUIPreset };
34
+ //# sourceMappingURL=useFloatingUIPreset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFloatingUIPreset.js","sources":["../../../../../../src/hooks/useFloatingUIPreset.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { UseFloatingOptions } from '@floating-ui/react';\nimport {\n autoUpdate,\n flip,\n offset,\n shift,\n size,\n useFloating,\n} from '@floating-ui/react';\n\nexport const useFloatingUIPreset = ({\n middleware = [],\n placement,\n strategy,\n offset: offsetInPx = 10,\n}: Pick<UseFloatingOptions, 'placement' | 'strategy' | 'middleware'> & {\n offset?: number;\n}) => {\n const {\n refs,\n x,\n y,\n update,\n elements: { domReference, floating },\n context,\n } = useFloating({\n placement,\n strategy,\n middleware: [\n offset(offsetInPx),\n shift(),\n flip(),\n size({\n padding: 10,\n apply: ({ availableHeight, elements }) => {\n Object.assign(elements.floating.style, {\n maxHeight: `${availableHeight}px`,\n });\n },\n }),\n ...middleware,\n ],\n });\n\n // handle window resizing\n useEffect(() => {\n if (!domReference || !floating) return;\n\n const cleanup = autoUpdate(domReference, floating, update);\n\n return () => cleanup();\n }, [domReference, floating, update]);\n\n return { refs, x, y, domReference, floating, strategy, context };\n};\n"],"names":[],"mappings":";;;MAWa,mBAAmB,GAAG,CAAC,EAClC,UAAU,GAAG,EAAE,EACf,SAAS,EACT,QAAQ,EACR,MAAM,EAAE,UAAU,GAAG,EAAE,GAGxB,KAAI;IACH,MAAM,EACJ,IAAI,EACJ,CAAC,EACD,CAAC,EACD,MAAM,EACN,QAAQ,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,EACpC,OAAO,GACR,GAAG,WAAW,CAAC;QACd,SAAS;QACT,QAAQ;AACR,QAAA,UAAU,EAAE;YACV,MAAM,CAAC,UAAU,CAAC;AAClB,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,EAAE;AACN,YAAA,IAAI,CAAC;AACH,gBAAA,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAI;oBACvC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE;wBACrC,SAAS,EAAE,CAAA,EAAG,eAAe,CAAA,EAAA,CAAI;AAClC,qBAAA,CAAC;gBACJ,CAAC;aACF,CAAC;AACF,YAAA,GAAG,UAAU;AACd,SAAA;AACF,KAAA,CAAC;;IAGF,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,YAAY,IAAI,CAAC,QAAQ;YAAE;QAEhC,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC;AAE1D,QAAA,OAAO,MAAM,OAAO,EAAE;IACxB,CAAC,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAEpC,IAAA,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE;AAClE;;;;"}
@@ -0,0 +1,138 @@
1
+ import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
2
+ import { getLoopbackStreams, videoLoggerSystem, DEFAULT_LOOPBACK_RECORDING_DURATION_MS, CallingState, waitForLoopbackStreams, LOOPBACK_STREAMS_WAIT_TIMEOUT_MS, enableLoopbackAudio, MediaStreamRecorder, clampLoopbackRecordingDuration } from '@stream-io/video-client';
3
+ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
4
+
5
+ const attachHiddenMediaSink = (stream) => {
6
+ const logger = videoLoggerSystem.getLogger('useLoopbackRecording');
7
+ const element = document.createElement('video');
8
+ element.muted = true;
9
+ element.autoplay = true;
10
+ element.playsInline = true;
11
+ element.srcObject = stream;
12
+ element.play().catch((error) => {
13
+ logger.warn('sink element failed to play; recording may be empty', error);
14
+ });
15
+ return () => {
16
+ element.pause();
17
+ element.srcObject = null;
18
+ };
19
+ };
20
+ /** Records SFU loopback streams for pre-call testing. */
21
+ const useLoopbackRecording = () => {
22
+ const call = useCall();
23
+ const { useCallCallingState, useParticipantCount, useLocalParticipant } = useCallStateHooks();
24
+ const callingState = useCallCallingState();
25
+ const participantCount = useParticipantCount();
26
+ const localParticipant = useLocalParticipant();
27
+ const [recordingState, setRecordingState] = useState('idle');
28
+ const recordingStateRef = useRef('idle');
29
+ const isMountedRef = useRef(true);
30
+ const awaitAbortRef = useRef(null);
31
+ const stopRef = useRef(null);
32
+ const runRef = useRef(null);
33
+ const loopbackStreams = useMemo(() => getLoopbackStreams(call, localParticipant), [call, localParticipant]);
34
+ const updateState = useCallback((next) => {
35
+ recordingStateRef.current = next;
36
+ if (isMountedRef.current) {
37
+ setRecordingState(next);
38
+ }
39
+ }, []);
40
+ const stopRecording = useCallback(async () => {
41
+ const current = recordingStateRef.current;
42
+ if (current === 'idle')
43
+ return;
44
+ if (current === 'awaiting-streams') {
45
+ videoLoggerSystem
46
+ .getLogger('useLoopbackRecording')
47
+ .debug('aborting the awaiting-streams wait');
48
+ awaitAbortRef.current?.abort();
49
+ return;
50
+ }
51
+ stopRef.current?.();
52
+ await runRef.current;
53
+ }, []);
54
+ const startRecording = useCallback(async ({ includeVideo = true, maxDurationMs = DEFAULT_LOOPBACK_RECORDING_DURATION_MS, } = {}) => {
55
+ const logger = videoLoggerSystem.getLogger('useLoopbackRecording');
56
+ if (!call)
57
+ return null;
58
+ if (recordingStateRef.current !== 'idle') {
59
+ logger.warn('a recording is already running');
60
+ return null;
61
+ }
62
+ if (call.state.participantCount > 1) {
63
+ logger.warn('cannot start recording with other participants present');
64
+ return null;
65
+ }
66
+ const run = async () => {
67
+ awaitAbortRef.current = new AbortController();
68
+ updateState('awaiting-streams');
69
+ try {
70
+ const tracks = await waitForLoopbackStreams(call, {
71
+ includeVideo,
72
+ signal: awaitAbortRef.current.signal,
73
+ timeoutMs: LOOPBACK_STREAMS_WAIT_TIMEOUT_MS,
74
+ });
75
+ if (tracks === null) {
76
+ logger.debug('loopback stream wait was aborted');
77
+ return null;
78
+ }
79
+ updateState('recording');
80
+ const restoreAudio = enableLoopbackAudio(tracks);
81
+ const stream = new MediaStream([tracks.audioTrack, tracks.videoTrack].filter((track) => !!track));
82
+ const detachSink = attachHiddenMediaSink(stream);
83
+ const recorder = new MediaStreamRecorder(stream, {
84
+ maxDurationMs: clampLoopbackRecordingDuration(maxDurationMs),
85
+ });
86
+ stopRef.current = recorder.stop;
87
+ try {
88
+ return await recorder.start();
89
+ }
90
+ finally {
91
+ detachSink();
92
+ restoreAudio();
93
+ }
94
+ }
95
+ catch (err) {
96
+ logger.error('loopback recording failed', err);
97
+ throw err;
98
+ }
99
+ finally {
100
+ awaitAbortRef.current = null;
101
+ stopRef.current = null;
102
+ updateState('idle');
103
+ }
104
+ };
105
+ const promise = run();
106
+ runRef.current = promise.catch(() => undefined);
107
+ return promise;
108
+ }, [call, updateState]);
109
+ useEffect(() => {
110
+ if (callingState === CallingState.LEFT ||
111
+ callingState === CallingState.IDLE) {
112
+ stopRecording().catch(() => { });
113
+ }
114
+ }, [callingState, stopRecording]);
115
+ useEffect(() => {
116
+ if (recordingState !== 'idle' && participantCount > 1) {
117
+ stopRecording().catch(() => { });
118
+ }
119
+ }, [participantCount, recordingState, stopRecording]);
120
+ useEffect(() => {
121
+ isMountedRef.current = true;
122
+ return () => {
123
+ isMountedRef.current = false;
124
+ awaitAbortRef.current?.abort('the component unmounted');
125
+ stopRef.current?.();
126
+ };
127
+ }, []);
128
+ return {
129
+ startRecording,
130
+ stopRecording,
131
+ recordingState,
132
+ loopbackVideoStream: loopbackStreams.loopbackVideoStream,
133
+ loopbackAudioStream: loopbackStreams.loopbackAudioStream,
134
+ };
135
+ };
136
+
137
+ export { useLoopbackRecording };
138
+ //# sourceMappingURL=useLoopbackRecording.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLoopbackRecording.js","sources":["../../../../../../src/hooks/useLoopbackRecording.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CallingState,\n DEFAULT_LOOPBACK_RECORDING_DURATION_MS,\n LOOPBACK_STREAMS_WAIT_TIMEOUT_MS,\n MediaStreamRecorder,\n clampLoopbackRecordingDuration,\n getLoopbackStreams,\n type MediaRecordingResult,\n videoLoggerSystem,\n waitForLoopbackStreams,\n enableLoopbackAudio,\n type LoopbackStreams,\n} from '@stream-io/video-client';\nimport { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';\n\n/** Lifecycle phase of a loopback recording. */\nexport type LoopbackRecordingState = 'idle' | 'awaiting-streams' | 'recording';\n\nconst attachHiddenMediaSink = (stream: MediaStream): (() => void) => {\n const logger = videoLoggerSystem.getLogger('useLoopbackRecording');\n const element = document.createElement('video');\n element.muted = true;\n element.autoplay = true;\n element.playsInline = true;\n element.srcObject = stream;\n\n element.play().catch((error) => {\n logger.warn('sink element failed to play; recording may be empty', error);\n });\n\n return () => {\n element.pause();\n element.srcObject = null;\n };\n};\n\nexport type StartLoopbackRecordingOptions = {\n /** Whether to include loopback video. Audio is always recorded. */\n includeVideo?: boolean;\n /** Maximum recording duration in milliseconds. */\n maxDurationMs?: number;\n};\n\nexport type UseLoopbackRecordingResult = {\n /** Starts recording and resolves with the recorded blob. */\n startRecording: (\n options?: StartLoopbackRecordingOptions,\n ) => Promise<MediaRecordingResult | null>;\n /** Stops the current recording or stream wait. */\n stopRecording: () => Promise<void>;\n /** Lifecycle phase of the recording. */\n recordingState: LoopbackRecordingState;\n /** SFU-echoed video stream on the local participant. */\n loopbackVideoStream?: MediaStream;\n /** SFU-echoed audio stream on the local participant. */\n loopbackAudioStream?: MediaStream;\n};\n\n/** Records SFU loopback streams for pre-call testing. */\nexport const useLoopbackRecording = (): UseLoopbackRecordingResult => {\n const call = useCall();\n const { useCallCallingState, useParticipantCount, useLocalParticipant } =\n useCallStateHooks();\n const callingState = useCallCallingState();\n const participantCount = useParticipantCount();\n const localParticipant = useLocalParticipant();\n\n const [recordingState, setRecordingState] =\n useState<LoopbackRecordingState>('idle');\n\n const recordingStateRef = useRef<LoopbackRecordingState>('idle');\n const isMountedRef = useRef(true);\n const awaitAbortRef = useRef<AbortController | null>(null);\n const stopRef = useRef<(() => void) | null>(null);\n const runRef = useRef<Promise<unknown> | null>(null);\n\n const loopbackStreams = useMemo<LoopbackStreams>(\n () => getLoopbackStreams(call, localParticipant),\n [call, localParticipant],\n );\n\n const updateState = useCallback((next: LoopbackRecordingState) => {\n recordingStateRef.current = next;\n if (isMountedRef.current) {\n setRecordingState(next);\n }\n }, []);\n\n const stopRecording = useCallback(async (): Promise<void> => {\n const current = recordingStateRef.current;\n if (current === 'idle') return;\n\n if (current === 'awaiting-streams') {\n videoLoggerSystem\n .getLogger('useLoopbackRecording')\n .debug('aborting the awaiting-streams wait');\n awaitAbortRef.current?.abort();\n return;\n }\n\n stopRef.current?.();\n await runRef.current;\n }, []);\n\n const startRecording = useCallback(\n async ({\n includeVideo = true,\n maxDurationMs = DEFAULT_LOOPBACK_RECORDING_DURATION_MS,\n }: StartLoopbackRecordingOptions = {}): Promise<MediaRecordingResult | null> => {\n const logger = videoLoggerSystem.getLogger('useLoopbackRecording');\n if (!call) return null;\n\n if (recordingStateRef.current !== 'idle') {\n logger.warn('a recording is already running');\n return null;\n }\n\n if (call.state.participantCount > 1) {\n logger.warn('cannot start recording with other participants present');\n return null;\n }\n\n const run = async (): Promise<MediaRecordingResult | null> => {\n awaitAbortRef.current = new AbortController();\n updateState('awaiting-streams');\n\n try {\n const tracks = await waitForLoopbackStreams(call, {\n includeVideo,\n signal: awaitAbortRef.current.signal,\n timeoutMs: LOOPBACK_STREAMS_WAIT_TIMEOUT_MS,\n });\n\n if (tracks === null) {\n logger.debug('loopback stream wait was aborted');\n return null;\n }\n\n updateState('recording');\n\n const restoreAudio = enableLoopbackAudio(tracks);\n const stream = new MediaStream(\n [tracks.audioTrack, tracks.videoTrack].filter(\n (track): track is MediaStreamTrack => !!track,\n ),\n );\n const detachSink = attachHiddenMediaSink(stream);\n const recorder = new MediaStreamRecorder(stream, {\n maxDurationMs: clampLoopbackRecordingDuration(maxDurationMs),\n });\n stopRef.current = recorder.stop;\n try {\n return await recorder.start();\n } finally {\n detachSink();\n restoreAudio();\n }\n } catch (err) {\n logger.error('loopback recording failed', err);\n throw err;\n } finally {\n awaitAbortRef.current = null;\n stopRef.current = null;\n updateState('idle');\n }\n };\n\n const promise = run();\n runRef.current = promise.catch(() => undefined);\n return promise;\n },\n [call, updateState],\n );\n\n useEffect(() => {\n if (\n callingState === CallingState.LEFT ||\n callingState === CallingState.IDLE\n ) {\n stopRecording().catch(() => {});\n }\n }, [callingState, stopRecording]);\n\n useEffect(() => {\n if (recordingState !== 'idle' && participantCount > 1) {\n stopRecording().catch(() => {});\n }\n }, [participantCount, recordingState, stopRecording]);\n\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n awaitAbortRef.current?.abort('the component unmounted');\n stopRef.current?.();\n };\n }, []);\n\n return {\n startRecording,\n stopRecording,\n recordingState,\n loopbackVideoStream: loopbackStreams.loopbackVideoStream,\n loopbackAudioStream: loopbackStreams.loopbackAudioStream,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,135 @@
1
+ import { useRef, useCallback, useEffect } from 'react';
2
+ import { useCall } from '@stream-io/video-react-bindings';
3
+
4
+ const isFullScreenBlurPlatformSupported = () => {
5
+ if (typeof window === 'undefined' ||
6
+ typeof OffscreenCanvas === 'undefined' ||
7
+ typeof VideoFrame === 'undefined' ||
8
+ !window.WebGL2RenderingContext) {
9
+ return false;
10
+ }
11
+ try {
12
+ const canvas = new OffscreenCanvas(1, 1);
13
+ return !!canvas.getContext('webgl2', {
14
+ alpha: false,
15
+ antialias: false,
16
+ desynchronized: true,
17
+ });
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ };
23
+ const useModeration = (options) => {
24
+ const { duration = 5000 } = options || {};
25
+ const call = useCall();
26
+ const timeoutRef = useRef(null);
27
+ const processorRef = useRef(null);
28
+ const unregisterRef = useRef(null);
29
+ const blurModulePromise = useRef(null);
30
+ /**
31
+ * Lazily loads and caches the video-filters-web module.
32
+ */
33
+ const loadVideoFiltersWebModule = useCallback(() => {
34
+ if (!blurModulePromise.current) {
35
+ blurModulePromise.current = import('@stream-io/video-filters-web')
36
+ .then((module) => module.FullScreenBlur)
37
+ .catch((error) => {
38
+ console.error('[moderation] Failed to import blur module:', error);
39
+ throw error;
40
+ });
41
+ }
42
+ return blurModulePromise.current;
43
+ }, []);
44
+ const disableBlur = useCallback(() => {
45
+ if (timeoutRef.current) {
46
+ clearTimeout(timeoutRef.current);
47
+ timeoutRef.current = null;
48
+ }
49
+ unregisterRef
50
+ .current?.()
51
+ .catch((err) => console.error('[moderation] unregister error:', err));
52
+ unregisterRef.current = null;
53
+ }, []);
54
+ const handleFallback = useCallback(async () => {
55
+ try {
56
+ await call?.camera.disable();
57
+ }
58
+ catch (error) {
59
+ console.error('[moderation] Failed to disable camera:', error);
60
+ }
61
+ }, [call]);
62
+ useEffect(() => {
63
+ if (!call)
64
+ return;
65
+ return call.on('call.moderation_warning', async () => {
66
+ try {
67
+ await loadVideoFiltersWebModule();
68
+ }
69
+ catch (importErr) {
70
+ console.error('[moderation] Failed to import blur module:', importErr);
71
+ }
72
+ });
73
+ }, [call, loadVideoFiltersWebModule]);
74
+ useEffect(() => {
75
+ if (!call)
76
+ return;
77
+ return call.on('call.moderation_blur', async () => {
78
+ if (unregisterRef.current)
79
+ return;
80
+ let FullScreenBlurClass;
81
+ try {
82
+ FullScreenBlurClass = await loadVideoFiltersWebModule();
83
+ }
84
+ catch (importErr) {
85
+ console.error('[moderation] Failed to import blur module:', importErr);
86
+ await handleFallback();
87
+ return;
88
+ }
89
+ if (!isFullScreenBlurPlatformSupported()) {
90
+ console.warn('[moderation] Blur not supported on this platform');
91
+ await handleFallback();
92
+ return;
93
+ }
94
+ const { unregister } = call.camera.registerFilter((inputStream) => {
95
+ unregisterRef.current = unregister;
96
+ const outputPromise = new Promise(async (resolve, reject) => {
97
+ const [track] = inputStream.getVideoTracks();
98
+ let processor;
99
+ try {
100
+ processor = new FullScreenBlurClass(track);
101
+ processorRef.current = processor;
102
+ const result = await processor.start();
103
+ const output = new MediaStream([result]);
104
+ resolve(output);
105
+ if (duration > 0) {
106
+ timeoutRef.current = setTimeout(disableBlur, duration);
107
+ }
108
+ }
109
+ catch (error) {
110
+ reject(error);
111
+ console.error('[moderation] Processor init failed:', error);
112
+ await unregisterRef.current?.();
113
+ unregisterRef.current = null;
114
+ processorRef.current = null;
115
+ await handleFallback();
116
+ return;
117
+ }
118
+ });
119
+ return {
120
+ output: outputPromise,
121
+ stop: () => {
122
+ if (processorRef.current) {
123
+ processorRef.current.stop();
124
+ processorRef.current = null;
125
+ }
126
+ },
127
+ };
128
+ });
129
+ });
130
+ }, [call, loadVideoFiltersWebModule, disableBlur, handleFallback, duration]);
131
+ useEffect(() => disableBlur, [disableBlur]);
132
+ };
133
+
134
+ export { useModeration };
135
+ //# sourceMappingURL=useModeration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModeration.js","sources":["../../../../../../src/hooks/useModeration.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { useCall } from '@stream-io/video-react-bindings';\n\ntype FullScreenBlurType =\n typeof import('@stream-io/video-filters-web').FullScreenBlur;\n\nconst isFullScreenBlurPlatformSupported = (): boolean => {\n if (\n typeof window === 'undefined' ||\n typeof OffscreenCanvas === 'undefined' ||\n typeof VideoFrame === 'undefined' ||\n !window.WebGL2RenderingContext\n ) {\n return false;\n }\n\n try {\n const canvas = new OffscreenCanvas(1, 1);\n return !!canvas.getContext('webgl2', {\n alpha: false,\n antialias: false,\n desynchronized: true,\n });\n } catch {\n return false;\n }\n};\n\nexport interface ModerationOptions {\n /**\n * How long the moderation effect should stay active before being disabled.\n * Set to `0` to keep it active indefinitely. Defaults to 5000 ms.\n */\n duration?: number;\n}\n\nexport const useModeration = (options?: ModerationOptions) => {\n const { duration = 5000 } = options || {};\n\n const call = useCall();\n\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const processorRef = useRef<InstanceType<FullScreenBlurType> | null>(null);\n const unregisterRef = useRef<(() => Promise<void>) | null>(null);\n\n const blurModulePromise = useRef<Promise<FullScreenBlurType> | null>(null);\n\n /**\n * Lazily loads and caches the video-filters-web module.\n */\n const loadVideoFiltersWebModule = useCallback(() => {\n if (!blurModulePromise.current) {\n blurModulePromise.current = import('@stream-io/video-filters-web')\n .then((module) => module.FullScreenBlur)\n .catch((error) => {\n console.error('[moderation] Failed to import blur module:', error);\n throw error;\n });\n }\n\n return blurModulePromise.current;\n }, []);\n\n const disableBlur = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n unregisterRef\n .current?.()\n .catch((err) => console.error('[moderation] unregister error:', err));\n\n unregisterRef.current = null;\n }, []);\n\n const handleFallback = useCallback(async () => {\n try {\n await call?.camera.disable();\n } catch (error) {\n console.error('[moderation] Failed to disable camera:', error);\n }\n }, [call]);\n\n useEffect(() => {\n if (!call) return;\n\n return call.on('call.moderation_warning', async () => {\n try {\n await loadVideoFiltersWebModule();\n } catch (importErr) {\n console.error('[moderation] Failed to import blur module:', importErr);\n }\n });\n }, [call, loadVideoFiltersWebModule]);\n\n useEffect(() => {\n if (!call) return;\n\n return call.on('call.moderation_blur', async () => {\n if (unregisterRef.current) return;\n\n let FullScreenBlurClass: FullScreenBlurType;\n\n try {\n FullScreenBlurClass = await loadVideoFiltersWebModule();\n } catch (importErr) {\n console.error('[moderation] Failed to import blur module:', importErr);\n await handleFallback();\n return;\n }\n\n if (!isFullScreenBlurPlatformSupported()) {\n console.warn('[moderation] Blur not supported on this platform');\n await handleFallback();\n return;\n }\n\n const { unregister } = call.camera.registerFilter((inputStream) => {\n unregisterRef.current = unregister;\n\n const outputPromise = new Promise<MediaStream>(\n async (resolve, reject) => {\n const [track] = inputStream.getVideoTracks();\n\n let processor: InstanceType<FullScreenBlurType>;\n\n try {\n processor = new FullScreenBlurClass(track);\n processorRef.current = processor;\n\n const result = await processor.start();\n const output = new MediaStream([result]);\n resolve(output);\n\n if (duration > 0) {\n timeoutRef.current = setTimeout(disableBlur, duration);\n }\n } catch (error) {\n reject(error);\n console.error('[moderation] Processor init failed:', error);\n\n await unregisterRef.current?.();\n unregisterRef.current = null;\n processorRef.current = null;\n\n await handleFallback();\n return;\n }\n },\n );\n\n return {\n output: outputPromise,\n stop: () => {\n if (processorRef.current) {\n processorRef.current.stop();\n processorRef.current = null;\n }\n },\n };\n });\n });\n }, [call, loadVideoFiltersWebModule, disableBlur, handleFallback, duration]);\n\n useEffect(() => disableBlur, [disableBlur]);\n};\n"],"names":[],"mappings":"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"}