@stream-io/video-react-sdk 1.36.1 → 1.37.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.
@@ -665,17 +665,13 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
665
665
  };
666
666
  Video$1.displayName = 'Video';
667
667
 
668
- const useTrackElementVisibility = ({ trackedElement, dynascaleManager: propsDynascaleManager, sessionId, trackType, }) => {
668
+ const useTrackElementVisibility = ({ trackedElement, sessionId, trackType, }) => {
669
669
  const call = videoReactBindings.useCall();
670
- const manager = propsDynascaleManager ?? call?.dynascaleManager;
671
670
  react.useEffect(() => {
672
- if (!trackedElement || !manager || !call || trackType === 'none')
671
+ if (!trackedElement || !call || trackType === 'none')
673
672
  return;
674
- const unobserve = manager.trackElementVisibility(trackedElement, sessionId, trackType);
675
- return () => {
676
- unobserve();
677
- };
678
- }, [trackedElement, manager, call, sessionId, trackType]);
673
+ return call.trackElementVisibility(trackedElement, sessionId, trackType);
674
+ }, [trackedElement, call, sessionId, trackType]);
679
675
  };
680
676
 
681
677
  const Avatar = ({ imageSrc, name, style, className, ...rest }) => {
@@ -1207,7 +1203,7 @@ const SpeakerTest = (props) => {
1207
1203
  const audioElementRef = react.useRef(null);
1208
1204
  const [isPlaying, setIsPlaying] = react.useState(false);
1209
1205
  const { t } = videoReactBindings.useI18n();
1210
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.36.1"}/assets/piano.mp3`, } = props;
1206
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.37.1"}/assets/piano.mp3`, } = props;
1211
1207
  // Update audio output device when selection changes
1212
1208
  react.useEffect(() => {
1213
1209
  const audio = audioElementRef.current;
@@ -1316,25 +1312,31 @@ react.forwardRef(function ToggleDeviceSettingsMenuButton({ menuShown }, ref) {
1316
1312
  const ToggleAudioPreviewButton = (props) => {
1317
1313
  const { caption, Menu = DeviceSelectorAudioInput, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1318
1314
  const { t } = videoReactBindings.useI18n();
1319
- const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
1315
+ const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1320
1316
  const { microphone, hasBrowserPermission, isPromptingPermission, optionsAwareIsMute, isTogglePending, } = useMicrophoneState({ optimisticUpdates });
1317
+ const localParticipant = useLocalParticipant();
1318
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.AUDIO);
1321
1319
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1322
1320
  const handleClick = createCallControlHandler(props, () => microphone.toggle());
1323
1321
  return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1324
1322
  ? t('Check your browser audio permissions')
1325
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1323
+ : isSystemMuted
1324
+ ? t('Microphone is paused by your system')
1325
+ : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1326
1326
  ? 'preview-audio-unmute-button'
1327
1327
  : 'preview-audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1328
1328
  setTooltipDisabled(shown);
1329
1329
  onMenuToggle?.(shown);
1330
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
1330
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }));
1331
1331
  };
1332
1332
  const ToggleAudioPublishingButton = (props) => {
1333
1333
  const { t } = videoReactBindings.useI18n();
1334
1334
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1335
1335
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.OwnCapability.SEND_AUDIO);
1336
- const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
1336
+ const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1337
1337
  const { microphone, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useMicrophoneState({ optimisticUpdates });
1338
+ const localParticipant = useLocalParticipant();
1339
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.AUDIO);
1338
1340
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1339
1341
  const handleClick = createCallControlHandler(props, async () => {
1340
1342
  if (!hasPermission) {
@@ -1348,37 +1350,45 @@ const ToggleAudioPublishingButton = (props) => {
1348
1350
  ? t('You have no permission to share your audio')
1349
1351
  : !hasBrowserPermission
1350
1352
  ? t('Check your browser mic permissions')
1351
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1353
+ : isSystemMuted
1354
+ ? t('Microphone is paused by your system')
1355
+ : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1352
1356
  !hasPermission ||
1353
1357
  // disable button while the toggle action is pending when not using optimistic updates
1354
1358
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'audio-unmute-button' : 'audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1355
1359
  setTooltipDisabled(shown);
1356
1360
  onMenuToggle?.(shown);
1357
- }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }) }) }));
1361
+ }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && hasPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }) }) }));
1358
1362
  };
1359
1363
 
1360
1364
  const ToggleVideoPreviewButton = (props) => {
1361
1365
  const { caption, Menu = DeviceSelectorVideo, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1362
1366
  const { t } = videoReactBindings.useI18n();
1363
- const { useCameraState } = videoReactBindings.useCallStateHooks();
1367
+ const { useCameraState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1364
1368
  const { camera, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useCameraState({ optimisticUpdates });
1369
+ const localParticipant = useLocalParticipant();
1370
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.VIDEO);
1365
1371
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1366
1372
  const handleClick = createCallControlHandler(props, () => camera.toggle());
1367
1373
  return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1368
1374
  ? t('Check your browser video permissions')
1369
- : (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
1375
+ : isSystemMuted
1376
+ ? t('Camera is paused by your system')
1377
+ : (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
1370
1378
  ? 'preview-video-unmute-button'
1371
1379
  : 'preview-video-mute-button', onClick: handleClick, disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1372
1380
  setTooltipDisabled(shown);
1373
1381
  onMenuToggle?.(shown);
1374
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
1382
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }));
1375
1383
  };
1376
1384
  const ToggleVideoPublishingButton = (props) => {
1377
1385
  const { t } = videoReactBindings.useI18n();
1378
1386
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorVideo, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1379
1387
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.OwnCapability.SEND_VIDEO);
1380
- const { useCameraState, useCallSettings } = videoReactBindings.useCallStateHooks();
1388
+ const { useCameraState, useCallSettings, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1381
1389
  const { camera, optionsAwareIsMute, hasBrowserPermission, isPromptingPermission, isTogglePending, } = useCameraState({ optimisticUpdates });
1390
+ const localParticipant = useLocalParticipant();
1391
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.VIDEO);
1382
1392
  const callSettings = useCallSettings();
1383
1393
  const isPublishingVideoAllowed = callSettings?.video.enabled;
1384
1394
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
@@ -1396,7 +1406,9 @@ const ToggleVideoPublishingButton = (props) => {
1396
1406
  ? t('Check your browser video permissions')
1397
1407
  : !isPublishingVideoAllowed
1398
1408
  ? t('Video publishing is disabled by the system')
1399
- : caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1409
+ : isSystemMuted
1410
+ ? t('Camera is paused by your system')
1411
+ : caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1400
1412
  !hasPermission ||
1401
1413
  !isPublishingVideoAllowed ||
1402
1414
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'video-unmute-button' : 'video-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
@@ -1404,7 +1416,10 @@ const ToggleVideoPublishingButton = (props) => {
1404
1416
  onMenuToggle?.(shown);
1405
1417
  }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
1406
1418
  !hasPermission ||
1407
- !isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }) }) }));
1419
+ !isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted &&
1420
+ hasBrowserPermission &&
1421
+ hasPermission &&
1422
+ isPublishingVideoAllowed && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }) }) }));
1408
1423
  };
1409
1424
 
1410
1425
  const EndCallMenu = (props) => {
@@ -1902,7 +1917,6 @@ const ParticipantView = react.forwardRef(function ParticipantView({ participant,
1902
1917
  const [trackedElement, setTrackedElement] = react.useState(null);
1903
1918
  const [contextVideoElement, setContextVideoElement] = react.useState(null);
1904
1919
  const [contextVideoPlaceholderElement, setContextVideoPlaceholderElement] = react.useState(null);
1905
- // TODO: allow to pass custom ViewportTracker instance from props
1906
1920
  useTrackElementVisibility({
1907
1921
  sessionId,
1908
1922
  trackedElement,
@@ -2033,8 +2047,10 @@ var en = {
2033
2047
  Dismiss: Dismiss,
2034
2048
  "Microphone on": "Microphone on",
2035
2049
  "Microphone off": "Microphone off",
2050
+ "Microphone is paused by your system": "Microphone is paused by your system",
2036
2051
  "Camera on": "Camera on",
2037
2052
  "Camera off": "Camera off",
2053
+ "Camera is paused by your system": "Camera is paused by your system",
2038
2054
  "No camera found": "No camera found",
2039
2055
  "Video is disabled": "Video is disabled",
2040
2056
  Pinned: Pinned,
@@ -2421,7 +2437,7 @@ const PaginatedGridLayout = (props) => {
2421
2437
  if (!paginatedGridLayoutWrapperElement || !call)
2422
2438
  return;
2423
2439
  const cleanup = call.setViewport(paginatedGridLayoutWrapperElement);
2424
- return () => cleanup();
2440
+ return () => cleanup?.();
2425
2441
  }, [paginatedGridLayoutWrapperElement, call]);
2426
2442
  // only used to render video elements
2427
2443
  const participantGroups = react.useMemo(() => chunk(participants, groupSize), [participants, groupSize]);
@@ -2513,7 +2529,7 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
2513
2529
  if (!participantsBarWrapperElement || !call)
2514
2530
  return;
2515
2531
  const cleanup = call.setViewport(participantsBarWrapperElement);
2516
- return () => cleanup();
2532
+ return () => cleanup?.();
2517
2533
  }, [participantsBarWrapperElement, call]);
2518
2534
  const isOneOnOneCall = allParticipants.length === 2;
2519
2535
  useSpeakerLayoutSortPreset(call, isOneOnOneCall);