@stream-io/video-react-sdk 0.3.28 → 0.3.30

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 (44) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +7 -5
  3. package/dist/css/styles.css +100 -0
  4. package/dist/css/styles.css.map +1 -1
  5. package/dist/src/components/CallParticipantsList/CallParticipantListingItem.js +2 -1
  6. package/dist/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -1
  7. package/dist/src/core/components/Audio/ParticipantsAudio.d.ts +14 -0
  8. package/dist/src/core/components/Audio/ParticipantsAudio.js +11 -0
  9. package/dist/src/core/components/Audio/ParticipantsAudio.js.map +1 -0
  10. package/dist/src/core/components/Audio/index.d.ts +1 -0
  11. package/dist/src/core/components/Audio/index.js +1 -0
  12. package/dist/src/core/components/Audio/index.js.map +1 -1
  13. package/dist/src/core/components/CallLayout/LivestreamLayout.d.ts +39 -0
  14. package/dist/src/core/components/CallLayout/LivestreamLayout.js +91 -0
  15. package/dist/src/core/components/CallLayout/LivestreamLayout.js.map +1 -0
  16. package/dist/src/core/components/CallLayout/PaginatedGridLayout.js +5 -3
  17. package/dist/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -1
  18. package/dist/src/core/components/CallLayout/SpeakerLayout.d.ts +7 -2
  19. package/dist/src/core/components/CallLayout/SpeakerLayout.js +32 -37
  20. package/dist/src/core/components/CallLayout/SpeakerLayout.js.map +1 -1
  21. package/dist/src/core/components/CallLayout/hooks.d.ts +3 -0
  22. package/dist/src/core/components/CallLayout/hooks.js +41 -0
  23. package/dist/src/core/components/CallLayout/hooks.js.map +1 -0
  24. package/dist/src/core/components/CallLayout/index.d.ts +1 -0
  25. package/dist/src/core/components/CallLayout/index.js +1 -0
  26. package/dist/src/core/components/CallLayout/index.js.map +1 -1
  27. package/dist/src/core/hooks/useCalculateHardLimit.d.ts +4 -0
  28. package/dist/src/core/hooks/useCalculateHardLimit.js +56 -0
  29. package/dist/src/core/hooks/useCalculateHardLimit.js.map +1 -0
  30. package/dist/src/translations/en.json +1 -0
  31. package/dist/src/translations/index.d.ts +1 -0
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/package.json +4 -4
  35. package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +2 -1
  36. package/src/core/components/Audio/ParticipantsAudio.tsx +35 -0
  37. package/src/core/components/Audio/index.ts +1 -0
  38. package/src/core/components/CallLayout/LivestreamLayout.tsx +231 -0
  39. package/src/core/components/CallLayout/PaginatedGridLayout.tsx +33 -36
  40. package/src/core/components/CallLayout/SpeakerLayout.tsx +74 -56
  41. package/src/core/components/CallLayout/hooks.ts +54 -0
  42. package/src/core/components/CallLayout/index.ts +1 -0
  43. package/src/core/hooks/useCalculateHardLimit.ts +72 -0
  44. package/src/translations/en.json +2 -0
@@ -1,48 +1,50 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { CallTypes, combineComparators, defaultSortPreset, screenSharing, SfuModels, speakerLayoutSortPreset, } from '@stream-io/video-client';
3
+ import clsx from 'clsx';
4
+ import { SfuModels } from '@stream-io/video-client';
4
5
  import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
5
6
  import { DefaultParticipantViewUI, ParticipantView, } from '../ParticipantView';
6
7
  import { IconButton } from '../../../components';
7
8
  import { useHorizontalScrollPosition, useVerticalScrollPosition, } from '../../../hooks';
8
- import clsx from 'clsx';
9
+ import { useSpeakerLayoutSortPreset } from './hooks';
10
+ import { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';
11
+ import { ParticipantsAudio } from '../Audio';
9
12
  const DefaultParticipantViewUIBar = () => (_jsx(DefaultParticipantViewUI, { menuPlacement: "top-end" }));
10
13
  const DefaultParticipantViewUISpotlight = () => _jsx(DefaultParticipantViewUI, {});
11
- export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition = 'bottom', }) => {
14
+ export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition = 'bottom', participantsBarLimit, }) => {
12
15
  const call = useCall();
13
- const { useParticipants } = useCallStateHooks();
16
+ const { useParticipants, useRemoteParticipants } = useCallStateHooks();
14
17
  const [participantInSpotlight, ...otherParticipants] = useParticipants();
15
- const [scrollWrapper, setScrollWrapper] = useState(null);
16
- const isOneOnOneCall = otherParticipants.length === 1;
18
+ const remoteParticipants = useRemoteParticipants();
19
+ const [participantsBarWrapperElement, setParticipantsBarWrapperElement] = useState(null);
20
+ const [participantsBarElement, setParticipantsBarElement] = useState(null);
21
+ const [buttonsWrapperElement, setButtonsWrapperElement] = useState(null);
22
+ const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
23
+ const hardLimit = useCalculateHardLimit(buttonsWrapperElement, participantsBarElement, participantsBarLimit);
24
+ const isVertical = participantsBarPosition === 'left' || participantsBarPosition === 'right';
25
+ const isHorizontal = participantsBarPosition === 'top' || participantsBarPosition === 'bottom';
17
26
  useEffect(() => {
18
- if (!scrollWrapper || !call)
27
+ if (!participantsBarWrapperElement || !call)
19
28
  return;
20
- const cleanup = call.setViewport(scrollWrapper);
29
+ const cleanup = call.setViewport(participantsBarWrapperElement);
21
30
  return () => cleanup();
22
- }, [scrollWrapper, call]);
23
- useEffect(() => {
24
- if (!call)
25
- return;
26
- // always show the remote participant in the spotlight
27
- if (isOneOnOneCall) {
28
- call.setSortParticipantsBy(combineComparators(screenSharing, loggedIn));
29
- }
30
- else {
31
- call.setSortParticipantsBy(speakerLayoutSortPreset);
32
- }
33
- return () => {
34
- // reset the sorting to the default for the call type
35
- const callConfig = CallTypes.get(call.type);
36
- call.setSortParticipantsBy(callConfig.options.sortParticipantsBy || defaultSortPreset);
37
- };
38
- }, [call, isOneOnOneCall]);
31
+ }, [participantsBarWrapperElement, call]);
32
+ const isOneOnOneCall = otherParticipants.length === 1;
33
+ useSpeakerLayoutSortPreset(call, isOneOnOneCall);
34
+ let participantsWithAppliedLimit = otherParticipants;
35
+ if (typeof participantsBarLimit !== 'undefined') {
36
+ const hardLimitToApply = isVertical
37
+ ? hardLimit.vertical
38
+ : hardLimit.horizontal;
39
+ participantsWithAppliedLimit = otherParticipants.slice(0,
40
+ // subtract 1 if speaker is sharing screen as
41
+ // that one is rendered independently from otherParticipants array
42
+ hardLimitToApply - (isSpeakerScreenSharing ? 1 : 0));
43
+ }
39
44
  if (!call)
40
45
  return null;
41
- const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
42
- return (_jsx("div", Object.assign({ className: "str-video__speaker-layout__wrapper" }, { children: _jsxs("div", Object.assign({ className: clsx('str-video__speaker-layout', participantsBarPosition &&
43
- `str-video__speaker-layout--variant-${participantsBarPosition}`) }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__spotlight" }, { children: participantInSpotlight && (_jsx(ParticipantView, { participant: participantInSpotlight, muteAudio: isSpeakerScreenSharing, trackType: isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack', ParticipantViewUI: ParticipantViewUISpotlight, VideoPlaceholder: VideoPlaceholder })) })), otherParticipants.length > 0 && participantsBarPosition && (_jsxs("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-buttons-wrapper" }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-wrapper", ref: setScrollWrapper }, { children: _jsxs("div", Object.assign({ className: "str-video__speaker-layout__participants-bar" }, { children: [isSpeakerScreenSharing && (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participantInSpotlight, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder }) }), participantInSpotlight.sessionId)), otherParticipants.map((participant) => (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participant, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder }) }), participant.sessionId)))] })) })), (participantsBarPosition === 'left' ||
44
- participantsBarPosition === 'right') && (_jsx(VerticalScrollButtons, { scrollWrapper: scrollWrapper })), (participantsBarPosition === 'top' ||
45
- participantsBarPosition === 'bottom') && (_jsx(HorizontalScrollButtons, { scrollWrapper: scrollWrapper }))] })))] })) })));
46
+ return (_jsxs("div", Object.assign({ className: "str-video__speaker-layout__wrapper" }, { children: [_jsx(ParticipantsAudio, { participants: remoteParticipants }), _jsxs("div", Object.assign({ className: clsx('str-video__speaker-layout', participantsBarPosition &&
47
+ `str-video__speaker-layout--variant-${participantsBarPosition}`) }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__spotlight" }, { children: participantInSpotlight && (_jsx(ParticipantView, { participant: participantInSpotlight, muteAudio: true, trackType: isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack', ParticipantViewUI: ParticipantViewUISpotlight, VideoPlaceholder: VideoPlaceholder })) })), participantsWithAppliedLimit.length > 0 && participantsBarPosition && (_jsxs("div", Object.assign({ ref: setButtonsWrapperElement, className: "str-video__speaker-layout__participants-bar-buttons-wrapper" }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-wrapper", ref: setParticipantsBarWrapperElement }, { children: _jsxs("div", Object.assign({ ref: setParticipantsBarElement, className: "str-video__speaker-layout__participants-bar" }, { children: [isSpeakerScreenSharing && (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participantInSpotlight, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder, muteAudio: true }) }), participantInSpotlight.sessionId)), participantsWithAppliedLimit.map((participant) => (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participant, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder, muteAudio: true }) }), participant.sessionId)))] })) })), isVertical && (_jsx(VerticalScrollButtons, { scrollWrapper: participantsBarWrapperElement })), isHorizontal && (_jsx(HorizontalScrollButtons, { scrollWrapper: participantsBarWrapperElement }))] })))] }))] })));
46
48
  };
47
49
  const HorizontalScrollButtons = ({ scrollWrapper, }) => {
48
50
  const scrollPosition = useHorizontalScrollPosition(scrollWrapper);
@@ -65,11 +67,4 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
65
67
  return (_jsxs(_Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (_jsx(IconButton, { onClick: scrollTopClickHandler, icon: "caret-up", className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (_jsx(IconButton, { onClick: scrollBottomClickHandler, icon: "caret-down", className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
66
68
  };
67
69
  const hasScreenShare = (p) => !!(p === null || p === void 0 ? void 0 : p.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE));
68
- const loggedIn = (a, b) => {
69
- if (a.isLocalParticipant)
70
- return 1;
71
- if (b.isLocalParticipant)
72
- return -1;
73
- return 0;
74
- };
75
70
  //# sourceMappingURL=SpeakerLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Call } from '@stream-io/video-client';
2
+ export declare const usePaginatedLayoutSortPreset: (call: Call | undefined) => void;
3
+ export declare const useSpeakerLayoutSortPreset: (call: Call | undefined, isOneOnOneCall: boolean) => void;
@@ -0,0 +1,41 @@
1
+ import { useEffect } from 'react';
2
+ import { CallTypes, combineComparators, defaultSortPreset, paginatedLayoutSortPreset, screenSharing, speakerLayoutSortPreset, } from '@stream-io/video-client';
3
+ export const usePaginatedLayoutSortPreset = (call) => {
4
+ useEffect(() => {
5
+ if (!call)
6
+ return;
7
+ call.setSortParticipantsBy(paginatedLayoutSortPreset);
8
+ return () => {
9
+ resetSortPreset(call);
10
+ };
11
+ }, [call]);
12
+ };
13
+ export const useSpeakerLayoutSortPreset = (call, isOneOnOneCall) => {
14
+ useEffect(() => {
15
+ if (!call)
16
+ return;
17
+ // always show the remote participant in the spotlight
18
+ if (isOneOnOneCall) {
19
+ call.setSortParticipantsBy(combineComparators(screenSharing, loggedIn));
20
+ }
21
+ else {
22
+ call.setSortParticipantsBy(speakerLayoutSortPreset);
23
+ }
24
+ return () => {
25
+ resetSortPreset(call);
26
+ };
27
+ }, [call, isOneOnOneCall]);
28
+ };
29
+ const resetSortPreset = (call) => {
30
+ // reset the sorting to the default for the call type
31
+ const callConfig = CallTypes.get(call.type);
32
+ call.setSortParticipantsBy(callConfig.options.sortParticipantsBy || defaultSortPreset);
33
+ };
34
+ const loggedIn = (a, b) => {
35
+ if (a.isLocalParticipant)
36
+ return 1;
37
+ if (b.isLocalParticipant)
38
+ return -1;
39
+ return 0;
40
+ };
41
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAEL,SAAS,EACT,kBAAkB,EAElB,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,uBAAuB,GAExB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,IAAsB,EAAE,EAAE;IACrE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,IAAI,CAAC,qBAAqB,CAAC,yBAAyB,CAAC,CAAC;QACtD,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,IAAsB,EACtB,cAAuB,EACvB,EAAE;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,sDAAsD;QACtD,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;SACzE;aAAM;YACL,IAAI,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,CAAC;SACrD;QACD,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAU,EAAE,EAAE;IACrC,qDAAqD;IACrD,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,qBAAqB,CACxB,UAAU,CAAC,OAAO,CAAC,kBAAkB,IAAI,iBAAiB,CAC3D,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAuC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;IAC5D,IAAI,CAAC,CAAC,kBAAkB;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,CAAC,kBAAkB;QAAE,OAAO,CAAC,CAAC,CAAC;IACpC,OAAO,CAAC,CAAC;AACX,CAAC,CAAC"}
@@ -1,2 +1,3 @@
1
+ export * from './LivestreamLayout';
1
2
  export * from './PaginatedGridLayout';
2
3
  export * from './SpeakerLayout';
@@ -1,3 +1,4 @@
1
+ export * from './LivestreamLayout';
1
2
  export * from './PaginatedGridLayout';
2
3
  export * from './SpeakerLayout';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const useCalculateHardLimit: (wrapperElement: HTMLDivElement | null, hostElement: HTMLDivElement | null, limit?: 'dynamic' | number) => {
2
+ vertical: number;
3
+ horizontal: number;
4
+ };
@@ -0,0 +1,56 @@
1
+ import { useEffect, useState } from 'react';
2
+ export const useCalculateHardLimit = (
3
+ /**
4
+ * Element that stretches to 100% of the whole layout component
5
+ */
6
+ wrapperElement,
7
+ /**
8
+ * Element that directly hosts individual `ParticipantView` (or wrapper) elements
9
+ */
10
+ hostElement, limit) => {
11
+ const [calculatedLimit, setCalculatedLimit] = useState({
12
+ vertical: typeof limit === 'number' ? limit : 1,
13
+ horizontal: typeof limit === 'number' ? limit : 1,
14
+ });
15
+ useEffect(() => {
16
+ if (!hostElement ||
17
+ !wrapperElement ||
18
+ typeof limit === 'number' ||
19
+ typeof limit === 'undefined')
20
+ return;
21
+ let childWidth = null;
22
+ let childHeight = null;
23
+ const resizeObserver = new ResizeObserver((entries, observer) => {
24
+ // this part should ideally run as little times as possible
25
+ // get child measurements and disconnect
26
+ // does not consider dynamically sized children
27
+ // this hook is for SpeakerLayout use only, where children in the bar are fixed size
28
+ if (entries.length > 1) {
29
+ const child = hostElement.firstChild;
30
+ if (child) {
31
+ childHeight = child.clientHeight;
32
+ childWidth = child.clientWidth;
33
+ observer.unobserve(hostElement);
34
+ }
35
+ }
36
+ // keep the state at { vertical: 1, horizontal: 1 }
37
+ // until we get the proper child measurements
38
+ if (childHeight === null || childWidth === null)
39
+ return;
40
+ const vertical = Math.floor(wrapperElement.clientHeight / childHeight);
41
+ const horizontal = Math.floor(wrapperElement.clientWidth / childWidth);
42
+ setCalculatedLimit((pv) => {
43
+ if (pv.vertical !== vertical || pv.horizontal !== horizontal)
44
+ return { vertical, horizontal };
45
+ return pv;
46
+ });
47
+ });
48
+ resizeObserver.observe(wrapperElement);
49
+ resizeObserver.observe(hostElement);
50
+ return () => {
51
+ resizeObserver.disconnect();
52
+ };
53
+ }, [hostElement, limit, wrapperElement]);
54
+ return calculatedLimit;
55
+ };
56
+ //# sourceMappingURL=useCalculateHardLimit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCalculateHardLimit.js","sourceRoot":"","sources":["../../../../src/core/hooks/useCalculateHardLimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,CAAC,MAAM,qBAAqB,GAAG;AACnC;;GAEG;AACH,cAAqC;AACrC;;GAEG;AACH,WAAkC,EAClC,KAA0B,EAC1B,EAAE;IACF,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAGnD;QACD,QAAQ,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/C,UAAU,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAClD,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IACE,CAAC,WAAW;YACZ,CAAC,cAAc;YACf,OAAO,KAAK,KAAK,QAAQ;YACzB,OAAO,KAAK,KAAK,WAAW;YAE5B,OAAO;QAET,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,WAAW,GAAkB,IAAI,CAAC;QAEtC,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YAC9D,2DAA2D;YAC3D,wCAAwC;YACxC,+CAA+C;YAC/C,oFAAoF;YACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtB,MAAM,KAAK,GAAG,WAAW,CAAC,UAAgC,CAAC;gBAE3D,IAAI,KAAK,EAAE;oBACT,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC;oBACjC,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC;oBAC/B,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;iBACjC;aACF;YAED,mDAAmD;YACnD,6CAA6C;YAC7C,IAAI,WAAW,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI;gBAAE,OAAO;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC;YACvE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,GAAG,UAAU,CAAC,CAAC;YAEvE,kBAAkB,CAAC,CAAC,EAAE,EAAE,EAAE;gBACxB,IAAI,EAAE,CAAC,QAAQ,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,KAAK,UAAU;oBAC1D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;gBAClC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACvC,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEpC,OAAO,GAAG,EAAE;YACV,cAAc,CAAC,UAAU,EAAE,CAAC;QAC9B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;IAEzC,OAAO,eAAe,CAAC;AACzB,CAAC,CAAC"}
@@ -11,6 +11,7 @@
11
11
  "Speakers": "Speakers",
12
12
  "Video": "Video",
13
13
  "You are muted. Unmute to speak.": "You are muted. Unmute to speak.",
14
+ "Live": "Live",
14
15
  "You can now speak.": "You can now speak.",
15
16
  "Awaiting for an approval to speak.": "Awaiting for an approval to speak.",
16
17
  "You can no longer speak.": "You can no longer speak.",
@@ -12,6 +12,7 @@ export declare const translations: {
12
12
  Speakers: string;
13
13
  Video: string;
14
14
  "You are muted. Unmute to speak.": string;
15
+ Live: string;
15
16
  "You can now speak.": string;
16
17
  "Awaiting for an approval to speak.": string;
17
18
  "You can no longer speak.": string;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.3.28";
1
+ export declare const version = "0.3.30";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '0.3.28';
1
+ export const version = '0.3.30';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "0.3.28",
3
+ "version": "0.3.30",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -33,8 +33,8 @@
33
33
  "@nivo/core": "^0.80.0",
34
34
  "@nivo/line": "^0.80.0",
35
35
  "@stream-io/i18n": "^0.1.2",
36
- "@stream-io/video-client": "^0.3.24",
37
- "@stream-io/video-react-bindings": "^0.2.25",
36
+ "@stream-io/video-client": "^0.3.25",
37
+ "@stream-io/video-react-bindings": "^0.2.26",
38
38
  "clsx": "^1.2.1",
39
39
  "prop-types": "^15.8.1",
40
40
  "rxjs": "~7.8.1"
@@ -44,7 +44,7 @@
44
44
  "react-dom": "^18.0.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@stream-io/video-styling": "^0.1.9",
47
+ "@stream-io/video-styling": "^0.1.10",
48
48
  "@types/prop-types": "^15.7.5",
49
49
  "react": "^18.2.0",
50
50
  "react-dom": "^18.2.0",
@@ -211,11 +211,12 @@ export const ParticipantActionsContextMenu = ({
211
211
  };
212
212
 
213
213
  const toggleFullscreenMode = () => {
214
- if (!fullscreenModeOn)
214
+ if (!fullscreenModeOn) {
215
215
  return participantViewElement
216
216
  ?.requestFullscreen()
217
217
  .then(() => setFullscreenModeOn(true))
218
218
  .catch(console.error);
219
+ }
219
220
 
220
221
  document
221
222
  .exitFullscreen()
@@ -0,0 +1,35 @@
1
+ import { ComponentProps } from 'react';
2
+ import { SfuModels, StreamVideoParticipant } from '@stream-io/video-client';
3
+ import { Audio } from './Audio';
4
+
5
+ export type ParticipantsAudioProps = {
6
+ /**
7
+ * The participants to play audio for.
8
+ */
9
+ participants: StreamVideoParticipant[];
10
+
11
+ /**
12
+ * Props to pass to the underlying `Audio` components.
13
+ */
14
+ audioProps?: ComponentProps<typeof Audio>;
15
+ };
16
+
17
+ export const ParticipantsAudio = (props: ParticipantsAudioProps) => {
18
+ const { participants, audioProps } = props;
19
+ return (
20
+ <>
21
+ {participants.map(
22
+ (participant) =>
23
+ !participant.isLocalParticipant &&
24
+ participant.publishedTracks.includes(SfuModels.TrackType.AUDIO) &&
25
+ participant.audioStream && (
26
+ <Audio
27
+ {...audioProps}
28
+ participant={participant}
29
+ key={participant.sessionId}
30
+ />
31
+ ),
32
+ )}
33
+ </>
34
+ );
35
+ };
@@ -1 +1,2 @@
1
1
  export * from './Audio';
2
+ export * from './ParticipantsAudio';
@@ -0,0 +1,231 @@
1
+ import clsx from 'clsx';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import {
4
+ useCall,
5
+ useCallStateHooks,
6
+ useI18n,
7
+ } from '@stream-io/video-react-bindings';
8
+ import { SfuModels, StreamVideoParticipant } from '@stream-io/video-client';
9
+ import { ParticipantView, useParticipantViewContext } from '../ParticipantView';
10
+ import { ParticipantsAudio } from '../Audio';
11
+ import { usePaginatedLayoutSortPreset } from './hooks';
12
+
13
+ /**
14
+ * The props for the {@link LivestreamLayout} component.
15
+ */
16
+ export type LivestreamLayoutProps = {
17
+ /**
18
+ * Whether the livestream is muted. Defaults to `false`.
19
+ */
20
+ muted?: boolean;
21
+
22
+ /**
23
+ * Whether to show the participant count. Defaults to `true`.
24
+ */
25
+ showParticipantCount?: boolean;
26
+
27
+ /**
28
+ * Whether to enable fullscreen mode. Defaults to `true`.
29
+ */
30
+ enableFullScreen?: boolean;
31
+
32
+ /**
33
+ * Whether to show the duration of the call. Defaults to `true`.
34
+ */
35
+ showDuration?: boolean;
36
+
37
+ /**
38
+ * Whether to show the live badge. Defaults to `true`.
39
+ */
40
+ showLiveBadge?: boolean;
41
+
42
+ /**
43
+ * Whether to show the speaker name. Defaults to `false`.
44
+ */
45
+ showSpeakerName?: boolean;
46
+
47
+ /**
48
+ * The props to pass to the floating participant element.
49
+ */
50
+ floatingParticipantProps?: LivestreamLayoutProps & {
51
+ /**
52
+ * The position of the floating participant element. Defaults to `top-right`.
53
+ */
54
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
55
+ };
56
+ };
57
+
58
+ export const LivestreamLayout = (props: LivestreamLayoutProps) => {
59
+ const { useParticipants, useRemoteParticipants, useHasOngoingScreenShare } =
60
+ useCallStateHooks();
61
+ const call = useCall();
62
+ const [currentSpeaker, ...otherParticipants] = useParticipants();
63
+ const remoteParticipants = useRemoteParticipants();
64
+ const hasOngoingScreenShare = useHasOngoingScreenShare();
65
+ const presenter = hasOngoingScreenShare
66
+ ? hasScreenShare(currentSpeaker) && currentSpeaker
67
+ : otherParticipants.find(hasScreenShare);
68
+
69
+ usePaginatedLayoutSortPreset(call);
70
+
71
+ const Overlay = (
72
+ <ParticipantOverlay
73
+ showParticipantCount={props.showParticipantCount}
74
+ showDuration={props.showDuration}
75
+ showLiveBadge={props.showLiveBadge}
76
+ showSpeakerName={props.showSpeakerName}
77
+ />
78
+ );
79
+
80
+ const { floatingParticipantProps } = props;
81
+ const FloatingParticipantOverlay = hasOngoingScreenShare && (
82
+ <ParticipantOverlay
83
+ // these elements aren't needed for the video feed
84
+ showParticipantCount={
85
+ floatingParticipantProps?.showParticipantCount ?? false
86
+ }
87
+ showDuration={floatingParticipantProps?.showDuration ?? false}
88
+ showLiveBadge={floatingParticipantProps?.showLiveBadge ?? false}
89
+ showSpeakerName={floatingParticipantProps?.showSpeakerName ?? true}
90
+ />
91
+ );
92
+
93
+ return (
94
+ <div className="str-video__livestream-layout__wrapper">
95
+ <ParticipantsAudio participants={remoteParticipants} />
96
+ {hasOngoingScreenShare && presenter && (
97
+ <ParticipantView
98
+ className="str-video__livestream-layout__screen-share"
99
+ participant={presenter}
100
+ ParticipantViewUI={Overlay}
101
+ trackType="screenShareTrack"
102
+ muteAudio // audio is rendered by ParticipantsAudio
103
+ />
104
+ )}
105
+ <ParticipantView
106
+ className={clsx(
107
+ hasOngoingScreenShare &&
108
+ 'str-video__livestream-layout__floating-participant',
109
+ (hasOngoingScreenShare &&
110
+ floatingParticipantProps?.position &&
111
+ `str-video__livestream-layout__floating-participant--${floatingParticipantProps.position}`) ??
112
+ 'str-video__livestream-layout__floating-participant--top-right',
113
+ )}
114
+ participant={currentSpeaker}
115
+ ParticipantViewUI={FloatingParticipantOverlay || Overlay}
116
+ muteAudio // audio is rendered by ParticipantsAudio
117
+ />
118
+ </div>
119
+ );
120
+ };
121
+
122
+ const hasScreenShare = (p?: StreamVideoParticipant) =>
123
+ !!p?.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE);
124
+
125
+ const ParticipantOverlay = (props: {
126
+ enableFullScreen?: boolean;
127
+ showParticipantCount?: boolean;
128
+ showDuration?: boolean;
129
+ showLiveBadge?: boolean;
130
+ showSpeakerName?: boolean;
131
+ }) => {
132
+ const {
133
+ enableFullScreen = true,
134
+ showParticipantCount = true,
135
+ showDuration = true,
136
+ showLiveBadge = true,
137
+ showSpeakerName = false,
138
+ } = props;
139
+ const { participant } = useParticipantViewContext();
140
+ const { useParticipantCount } = useCallStateHooks();
141
+ const participantCount = useParticipantCount();
142
+ const duration = useUpdateCallDuration();
143
+ const toggleFullScreen = useToggleFullScreen();
144
+ const { t } = useI18n();
145
+ return (
146
+ <div className="str-video__livestream-layout__overlay">
147
+ <div className="str-video__livestream-layout__overlay__bar">
148
+ {showLiveBadge && (
149
+ <span className="str-video__livestream-layout__live-badge">
150
+ {t('Live')}
151
+ </span>
152
+ )}
153
+ {showParticipantCount && (
154
+ <span className="str-video__livestream-layout__viewers-count">
155
+ {participantCount}
156
+ </span>
157
+ )}
158
+ {showSpeakerName && (
159
+ <span
160
+ className="str-video__livestream-layout__speaker-name"
161
+ title={participant.name || participant.userId || ''}
162
+ >
163
+ {participant.name || participant.userId || ''}
164
+ </span>
165
+ )}
166
+ {showDuration && (
167
+ <span className="str-video__livestream-layout__duration">
168
+ {formatDuration(duration)}
169
+ </span>
170
+ )}
171
+ {enableFullScreen && (
172
+ <span
173
+ className="str-video__livestream-layout__go-fullscreen"
174
+ onClick={toggleFullScreen}
175
+ />
176
+ )}
177
+ </div>
178
+ </div>
179
+ );
180
+ };
181
+
182
+ const useUpdateCallDuration = () => {
183
+ const { useIsCallLive, useCallSession } = useCallStateHooks();
184
+ const isCallLive = useIsCallLive();
185
+ const session = useCallSession();
186
+ const [duration, setDuration] = useState(() => {
187
+ if (!session || !session.live_started_at) return 0;
188
+ const liveStartTime = new Date(session.live_started_at);
189
+ const now = new Date();
190
+ return Math.floor((now.getTime() - liveStartTime.getTime()) / 1000);
191
+ });
192
+
193
+ useEffect(() => {
194
+ if (!isCallLive) return;
195
+ const interval = setInterval(() => {
196
+ setDuration((d) => d + 1);
197
+ }, 1000);
198
+ return () => {
199
+ clearInterval(interval);
200
+ };
201
+ }, [isCallLive]);
202
+
203
+ return duration;
204
+ };
205
+
206
+ const useToggleFullScreen = () => {
207
+ const { participantViewElement } = useParticipantViewContext();
208
+ const [isFullscreen, setIsFullscreen] = useState(false);
209
+ return useCallback(() => {
210
+ if (isFullscreen) {
211
+ document.exitFullscreen().then(() => {
212
+ setIsFullscreen(false);
213
+ });
214
+ } else {
215
+ participantViewElement?.requestFullscreen().then(() => {
216
+ setIsFullscreen(true);
217
+ });
218
+ }
219
+ }, [isFullscreen, participantViewElement]);
220
+ };
221
+
222
+ const formatDuration = (durationInMs: number) => {
223
+ const days = Math.floor(durationInMs / 86400);
224
+ const hours = Math.floor(durationInMs / 3600);
225
+ const minutes = Math.floor((durationInMs % 3600) / 60);
226
+ const seconds = durationInMs % 60;
227
+
228
+ return `${days ? days + ' ' : ''}${hours ? hours + ':' : ''}${
229
+ minutes < 10 ? '0' : ''
230
+ }${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
231
+ };