@stream-io/video-react-sdk 0.4.7 → 0.4.8

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/dist/index.d.ts CHANGED
@@ -2,6 +2,5 @@ export * from '@stream-io/video-client';
2
2
  export * from '@stream-io/video-react-bindings';
3
3
  export * from './src/core';
4
4
  export * from './src/components';
5
- export * from './src/types';
6
5
  export * from './src/translations';
7
6
  export { useHorizontalScrollPosition, useVerticalScrollPosition, useRequestPermission, usePersistedDevicePreferences, } from './src/hooks';
package/dist/index.es.js CHANGED
@@ -3,7 +3,7 @@ export * from '@stream-io/video-client';
3
3
  import { useCall, useCallStateHooks, useHasPermissions, useI18n, Restricted, useConnectedUser, StreamCallProvider, StreamVideoProvider } from '@stream-io/video-react-bindings';
4
4
  export * from '@stream-io/video-react-bindings';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
- import { useState, useEffect, Fragment as Fragment$1, isValidElement, forwardRef, useLayoutEffect, useCallback, useRef, useMemo, createContext, useContext } from 'react';
6
+ import { useState, useEffect, Fragment as Fragment$1, isValidElement, forwardRef, useLayoutEffect, useCallback, useRef, createContext, useContext, useMemo } from 'react';
7
7
  import clsx from 'clsx';
8
8
  import { useFloating, offset, shift, flip, size, autoUpdate } from '@floating-ui/react';
9
9
  import { ResponsiveLine } from '@nivo/line';
@@ -1417,20 +1417,21 @@ const PermissionRequests = () => {
1417
1417
  const [expanded, setExpanded] = useState(false);
1418
1418
  const [permissionRequests, setPermissionRequests] = useState([]);
1419
1419
  const canUpdateCallPermissions = useHasPermissions(OwnCapability.UPDATE_CALL_PERMISSIONS);
1420
+ const localUserId = localParticipant?.userId;
1420
1421
  useEffect(() => {
1421
1422
  if (!call || !canUpdateCallPermissions)
1422
1423
  return;
1423
1424
  const unsubscribe = call.on('call.permission_request', (event) => {
1424
1425
  if (event.type !== 'call.permission_request')
1425
1426
  return;
1426
- if (event.user.id !== localParticipant?.userId) {
1427
+ if (event.user.id !== localUserId) {
1427
1428
  setPermissionRequests((requests) => [...requests, event].sort((a, b) => byNameOrId(a.user, b.user)));
1428
1429
  }
1429
1430
  });
1430
1431
  return () => {
1431
1432
  unsubscribe();
1432
1433
  };
1433
- }, [call, canUpdateCallPermissions, localParticipant]);
1434
+ }, [call, canUpdateCallPermissions, localUserId]);
1434
1435
  const handleUpdatePermission = (request, type) => {
1435
1436
  return async () => {
1436
1437
  const { user, permissions } = request;
@@ -1525,128 +1526,6 @@ const VideoPreview = ({ className, mirror = true, DisabledVideoPreview = Default
1525
1526
  return (jsx("div", { className: clsx('str-video__video-preview-container', className), children: contents }));
1526
1527
  };
1527
1528
 
1528
- const DebugParticipantPublishQuality = (props) => {
1529
- const { call, participant } = props;
1530
- const [quality, setQuality] = useState();
1531
- const [publishStats, setPublishStats] = useState(() => ({
1532
- f: true,
1533
- h: true,
1534
- q: true,
1535
- }));
1536
- useEffect(() => {
1537
- return call.on('changePublishQuality', (event) => {
1538
- if (event.eventPayload.oneofKind !== 'changePublishQuality')
1539
- return;
1540
- const { videoSenders } = event.eventPayload.changePublishQuality;
1541
- // FIXME OL: support additional layers (like screenshare)
1542
- const [videoLayer] = videoSenders.map(({ layers }) => {
1543
- return layers.map((l) => ({ [l.name]: l.active }));
1544
- });
1545
- setPublishStats((s) => ({
1546
- ...s,
1547
- ...videoLayer,
1548
- }));
1549
- });
1550
- }, [call]);
1551
- return (jsxs("select", { title: `Published tracks: ${JSON.stringify(publishStats)}`, value: quality, onChange: (e) => {
1552
- const value = e.target.value;
1553
- setQuality(value);
1554
- let w = 960;
1555
- let h = 540;
1556
- if (value === 'h') {
1557
- w = w / 2; // 480
1558
- h = h / 2; // 270
1559
- }
1560
- else if (value === 'q') {
1561
- w = w / 4; // 240
1562
- h = h / 4; // 135
1563
- }
1564
- call.updateSubscriptionsPartial('video', {
1565
- [participant.sessionId]: {
1566
- dimension: {
1567
- width: w,
1568
- height: h,
1569
- },
1570
- },
1571
- });
1572
- }, children: [jsx("option", { value: "f", children: "High (f)" }), jsx("option", { value: "h", children: "Medium (h)" }), jsx("option", { value: "q", children: "Low (q)" })] }));
1573
- };
1574
-
1575
- const DebugStatsView = (props) => {
1576
- const { call, mediaStream, sessionId, userId } = props;
1577
- const { useCallStatsReport } = useCallStateHooks();
1578
- const callStatsReport = useCallStatsReport();
1579
- useEffect(() => {
1580
- call.startReportingStatsFor(sessionId);
1581
- return () => {
1582
- call.stopReportingStatsFor(sessionId);
1583
- };
1584
- }, [call, sessionId]);
1585
- const reportForTracks = callStatsReport?.participants[sessionId];
1586
- const trackStats = reportForTracks?.flatMap((report) => report.streams);
1587
- const previousWidth = useRef({ f: 0, h: 0, q: 0 });
1588
- const previousHeight = useRef({ f: 0, h: 0, q: 0 });
1589
- trackStats?.forEach((track) => {
1590
- if (track.kind !== 'video')
1591
- return;
1592
- const { frameWidth = 0, frameHeight = 0, rid = '' } = track;
1593
- if (frameWidth !== previousWidth.current[rid] ||
1594
- frameHeight !== previousHeight.current[rid]) {
1595
- const trackSize = `${frameWidth}x${frameHeight}`;
1596
- console.log(`Track stats (${userId}/${sessionId}): ${rid}(${trackSize})`);
1597
- previousWidth.current[rid] = frameWidth;
1598
- previousHeight.current[rid] = frameHeight;
1599
- }
1600
- });
1601
- const { refs, strategy, y, x } = useFloatingUIPreset({
1602
- placement: 'top',
1603
- strategy: 'absolute',
1604
- });
1605
- const [isPopperOpen, setIsPopperOpen] = useState(false);
1606
- const [videoTrack] = mediaStream?.getVideoTracks() ?? [];
1607
- const settings = videoTrack?.getSettings();
1608
- return (jsxs(Fragment, { children: [jsx("span", { className: "str-video__debug__track-stats-icon", tabIndex: 0, ref: refs.setReference, title: settings &&
1609
- `${settings.width}x${settings.height}@${Math.round(settings.frameRate || 0)}`, onClick: () => {
1610
- setIsPopperOpen((v) => !v);
1611
- } }), isPopperOpen && (jsxs("div", { className: "str-video__debug__track-stats str-video__call-stats", ref: refs.setFloating, style: {
1612
- position: strategy,
1613
- top: y ?? 0,
1614
- left: x ?? 0,
1615
- overflowY: 'auto',
1616
- }, children: [jsx("h3", { children: "Participant stats" }), jsx("div", { className: "str-video__call-stats__card-container", children: trackStats
1617
- ?.map((track) => {
1618
- if (track.kind === 'video') {
1619
- return (jsx(StatCard, { label: `${track.kind}: ${track.codec} ` +
1620
- (track.rid ? ` (${track.rid})` : ''), value: `${track.frameWidth || 0}x${track.frameHeight || 0}@${track.framesPerSecond || 0}fps` }, `${track.rid}/${track.ssrc}/${track.codec}/${track.kind}`));
1621
- }
1622
- else if (track.kind === 'audio') {
1623
- return (jsx(StatCard, { label: track.codec || 'N/A', value: `Jitter: ${track.jitter || 0}ms` }, `${track.ssrc}/${track.codec}/${track.kind}`));
1624
- }
1625
- return null;
1626
- })
1627
- .filter(Boolean) }), reportForTracks?.map((report, index) => (jsx("pre", { children: JSON.stringify(unwrapStats(report.rawStats), null, 2) }, index)))] }))] }));
1628
- };
1629
- const unwrapStats = (rawStats) => {
1630
- const decodedStats = {};
1631
- rawStats?.forEach((s) => {
1632
- decodedStats[s.id] = s;
1633
- });
1634
- return decodedStats;
1635
- };
1636
-
1637
- const useQueryParams = () => {
1638
- return useMemo(() => typeof window === 'undefined'
1639
- ? null
1640
- : new URLSearchParams(window.location.search), []);
1641
- };
1642
- /**
1643
- * Internal purpose hook. Enables certain development mode tools.
1644
- */
1645
- const useIsDebugMode = () => {
1646
- const params = useQueryParams();
1647
- return !!params?.get('debug');
1648
- };
1649
-
1650
1529
  const ParticipantViewContext = createContext(undefined);
1651
1530
  const useParticipantViewContext = () => useContext(ParticipantViewContext);
1652
1531
 
@@ -1674,18 +1553,18 @@ const DefaultParticipantViewUI = ({ indicatorsVisible = true, menuPlacement = 'b
1674
1553
  };
1675
1554
  const ParticipantDetails = ({ indicatorsVisible = true, }) => {
1676
1555
  const { participant } = useParticipantViewContext();
1677
- const { isDominantSpeaker, isLocalParticipant, connectionQuality, publishedTracks, pin, sessionId, name, userId, videoStream, } = participant;
1556
+ const { isDominantSpeaker, isLocalParticipant, connectionQuality, publishedTracks, pin, sessionId, name, userId, } = participant;
1678
1557
  const call = useCall();
1558
+ const { t } = useI18n();
1679
1559
  const connectionQualityAsString = !!connectionQuality &&
1680
1560
  SfuModels.ConnectionQuality[connectionQuality].toLowerCase();
1681
1561
  const hasAudio = publishedTracks.includes(SfuModels.TrackType.AUDIO);
1682
1562
  const hasVideo = publishedTracks.includes(SfuModels.TrackType.VIDEO);
1683
1563
  const canUnpin = !!pin && pin.isLocalPin;
1684
- const isDebugMode = useIsDebugMode();
1685
- return (jsxs("div", { className: "str-video__participant-details", children: [jsxs("span", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isDominantSpeaker && (jsx("span", { className: "str-video__participant-details__name--dominant_speaker", title: "Dominant speaker" })), indicatorsVisible && (jsx(Notification, { isVisible: isLocalParticipant &&
1686
- connectionQuality === SfuModels.ConnectionQuality.POOR, message: "Poor connection quality. Please check your internet connection.", children: connectionQualityAsString && (jsx("span", { className: clsx('str-video__participant-details__connection-quality', `str-video__participant-details__connection-quality--${connectionQualityAsString}`), title: connectionQualityAsString })) })), indicatorsVisible && !hasAudio && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && !hasVideo && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && canUnpin && (
1687
- // TODO: remove this monstrosity once we have a proper design
1688
- jsx("span", { title: "Unpin", onClick: () => call?.unpin(sessionId), style: { cursor: 'pointer' }, className: "str-video__participant-details__name--pinned" }))] }), isDebugMode && (jsxs(Fragment, { children: [jsx(DebugParticipantPublishQuality, { participant: participant, call: call }), jsx(DebugStatsView, { call: call, sessionId: sessionId, userId: userId, mediaStream: videoStream })] }))] }));
1564
+ return (jsx("div", { className: "str-video__participant-details", children: jsxs("span", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isDominantSpeaker && (jsx("span", { className: "str-video__participant-details__name--dominant_speaker", title: t('Dominant speaker') })), indicatorsVisible && (jsx(Notification, { isVisible: isLocalParticipant &&
1565
+ connectionQuality === SfuModels.ConnectionQuality.POOR, message: t('Poor connection quality'), children: connectionQualityAsString && (jsx("span", { className: clsx('str-video__participant-details__connection-quality', `str-video__participant-details__connection-quality--${connectionQualityAsString}`), title: connectionQualityAsString })) })), indicatorsVisible && !hasAudio && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && !hasVideo && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && canUnpin && (
1566
+ // TODO: remove this monstrosity once we have a proper design
1567
+ jsx("span", { title: t('Unpin'), onClick: () => call?.unpin(sessionId), style: { cursor: 'pointer' }, className: "str-video__participant-details__name--pinned" }))] }) }));
1689
1568
  };
1690
1569
 
1691
1570
  const ParticipantView = forwardRef(({ participant, trackType = 'videoTrack', muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) => {
@@ -1825,6 +1704,8 @@ var en = {
1825
1704
  Leave: Leave,
1826
1705
  "{{ direction }} fullscreen": "{{ direction }} fullscreen",
1827
1706
  "{{ direction }} picture-in-picture": "{{ direction }} picture-in-picture",
1707
+ "Dominant Speaker": "Dominant Speaker",
1708
+ "Poor connection quality": "Poor connection quality. Please check your internet connection.",
1828
1709
  Participants: Participants,
1829
1710
  Anonymous: Anonymous,
1830
1711
  "No participants found": "No participants found",
@@ -2114,7 +1995,7 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
2114
1995
  };
2115
1996
  const hasScreenShare = (p) => !!p?.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE);
2116
1997
 
2117
- const [major, minor, patch] = ("0.4.7" ).split('.');
1998
+ const [major, minor, patch] = ("0.4.8" ).split('.');
2118
1999
  setSdkInfo({
2119
2000
  type: SfuModels.SdkType.REACT,
2120
2001
  major,
@@ -2122,5 +2003,5 @@ setSdkInfo({
2122
2003
  patch,
2123
2004
  });
2124
2005
 
2125
- export { AcceptCallButton, Audio, Avatar, AvatarFallback, BaseVideo, CallControls, CallParticipantListing, CallParticipantListingItem, CallParticipantsList, CallPreview, CallRecordingList, CallRecordingListHeader, CallRecordingListItem, CallStatsButton, CancelCallButton, CompositeButton, CopyToClipboardButton, CopyToClipboardButtonWithPopup, DefaultParticipantViewUI, DefaultReactionsMenu, DefaultScreenShareOverlay, DefaultVideoPlaceholder, DeviceSelector, DeviceSelectorAudioInput, DeviceSelectorAudioOutput, DeviceSelectorVideo, DeviceSettings, EmptyCallRecordingListing, GenericMenu, GenericMenuButtonItem, Icon, IconButton, LivestreamLayout, LoadingCallRecordingListing, LoadingIndicator, MenuToggle, Notification, PaginatedGridLayout, ParticipantActionsContextMenu, ParticipantDetails, ParticipantView, ParticipantViewContext, ParticipantsAudio, PermissionNotification, PermissionRequestList, PermissionRequests, ReactionsButton, RecordCallButton, RingingCall, RingingCallControls, ScreenShareButton, SearchInput, SearchResults, SpeakerLayout, SpeakingWhileMutedNotification, StreamCall, StreamTheme, StreamVideo, TextButton, ToggleAudioOutputButton, ToggleAudioPreviewButton, ToggleAudioPublishingButton, ToggleVideoPreviewButton, ToggleVideoPublishingButton, Tooltip, Video$1 as Video, VideoPreview, WithTooltip, defaultReactions, translations, useHasBrowserPermissions, useHorizontalScrollPosition, useParticipantViewContext, usePersistedDevicePreferences, useRequestPermission, useTrackElementVisibility, useVerticalScrollPosition };
2006
+ export { AcceptCallButton, Audio, Avatar, AvatarFallback, BaseVideo, CallControls, CallParticipantListing, CallParticipantListingItem, CallParticipantsList, CallPreview, CallRecordingList, CallRecordingListHeader, CallRecordingListItem, CallStats, CallStatsButton, CallStatsLatencyChart, CancelCallButton, CompositeButton, CopyToClipboardButton, CopyToClipboardButtonWithPopup, DefaultParticipantViewUI, DefaultReactionsMenu, DefaultScreenShareOverlay, DefaultVideoPlaceholder, DeviceSelector, DeviceSelectorAudioInput, DeviceSelectorAudioOutput, DeviceSelectorVideo, DeviceSettings, EmptyCallRecordingListing, GenericMenu, GenericMenuButtonItem, Icon, IconButton, LivestreamLayout, LoadingCallRecordingListing, LoadingIndicator, MenuToggle, Notification, PaginatedGridLayout, ParticipantActionsContextMenu, ParticipantDetails, ParticipantView, ParticipantViewContext, ParticipantsAudio, PermissionNotification, PermissionRequestList, PermissionRequests, ReactionsButton, RecordCallButton, RingingCall, RingingCallControls, ScreenShareButton, SearchInput, SearchResults, SpeakerLayout, SpeakingWhileMutedNotification, StatCard, StreamCall, StreamTheme, StreamVideo, TextButton, ToggleAudioOutputButton, ToggleAudioPreviewButton, ToggleAudioPublishingButton, ToggleVideoPreviewButton, ToggleVideoPublishingButton, Tooltip, Video$1 as Video, VideoPreview, WithTooltip, defaultReactions, translations, useHasBrowserPermissions, useHorizontalScrollPosition, useParticipantViewContext, usePersistedDevicePreferences, useRequestPermission, useTrackElementVisibility, useVerticalScrollPosition };
2126
2007
  //# sourceMappingURL=index.es.js.map