@stream-io/video-react-sdk 0.3.29 → 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.
- package/CHANGELOG.md +12 -0
- package/README.md +7 -5
- package/dist/css/styles.css +100 -0
- package/dist/css/styles.css.map +1 -1
- package/dist/src/components/CallParticipantsList/CallParticipantListingItem.js +2 -1
- package/dist/src/components/CallParticipantsList/CallParticipantListingItem.js.map +1 -1
- package/dist/src/core/components/Audio/ParticipantsAudio.d.ts +14 -0
- package/dist/src/core/components/Audio/ParticipantsAudio.js +11 -0
- package/dist/src/core/components/Audio/ParticipantsAudio.js.map +1 -0
- package/dist/src/core/components/Audio/index.d.ts +1 -0
- package/dist/src/core/components/Audio/index.js +1 -0
- package/dist/src/core/components/Audio/index.js.map +1 -1
- package/dist/src/core/components/CallLayout/LivestreamLayout.d.ts +39 -0
- package/dist/src/core/components/CallLayout/LivestreamLayout.js +91 -0
- package/dist/src/core/components/CallLayout/LivestreamLayout.js.map +1 -0
- package/dist/src/core/components/CallLayout/PaginatedGridLayout.js +4 -2
- package/dist/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -1
- package/dist/src/core/components/CallLayout/SpeakerLayout.d.ts +1 -1
- package/dist/src/core/components/CallLayout/SpeakerLayout.js +7 -28
- package/dist/src/core/components/CallLayout/SpeakerLayout.js.map +1 -1
- package/dist/src/core/components/CallLayout/hooks.d.ts +3 -0
- package/dist/src/core/components/CallLayout/hooks.js +41 -0
- package/dist/src/core/components/CallLayout/hooks.js.map +1 -0
- package/dist/src/core/components/CallLayout/index.d.ts +1 -0
- package/dist/src/core/components/CallLayout/index.js +1 -0
- package/dist/src/core/components/CallLayout/index.js.map +1 -1
- package/dist/src/translations/en.json +1 -0
- package/dist/src/translations/index.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +2 -1
- package/src/core/components/Audio/ParticipantsAudio.tsx +35 -0
- package/src/core/components/Audio/index.ts +1 -0
- package/src/core/components/CallLayout/LivestreamLayout.tsx +231 -0
- package/src/core/components/CallLayout/PaginatedGridLayout.tsx +5 -4
- package/src/core/components/CallLayout/SpeakerLayout.tsx +8 -40
- package/src/core/components/CallLayout/hooks.ts +54 -0
- package/src/core/components/CallLayout/index.ts +1 -0
- package/src/translations/en.json +2 -0
|
@@ -1,13 +1,14 @@
|
|
|
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';
|
|
4
|
-
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
5
3
|
import clsx from 'clsx';
|
|
4
|
+
import { SfuModels } from '@stream-io/video-client';
|
|
5
|
+
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
6
6
|
import { DefaultParticipantViewUI, ParticipantView, } from '../ParticipantView';
|
|
7
7
|
import { IconButton } from '../../../components';
|
|
8
8
|
import { useHorizontalScrollPosition, useVerticalScrollPosition, } from '../../../hooks';
|
|
9
|
+
import { useSpeakerLayoutSortPreset } from './hooks';
|
|
9
10
|
import { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';
|
|
10
|
-
import {
|
|
11
|
+
import { ParticipantsAudio } from '../Audio';
|
|
11
12
|
const DefaultParticipantViewUIBar = () => (_jsx(DefaultParticipantViewUI, { menuPlacement: "top-end" }));
|
|
12
13
|
const DefaultParticipantViewUISpotlight = () => _jsx(DefaultParticipantViewUI, {});
|
|
13
14
|
export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition = 'bottom', participantsBarLimit, }) => {
|
|
@@ -22,29 +23,14 @@ export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIB
|
|
|
22
23
|
const hardLimit = useCalculateHardLimit(buttonsWrapperElement, participantsBarElement, participantsBarLimit);
|
|
23
24
|
const isVertical = participantsBarPosition === 'left' || participantsBarPosition === 'right';
|
|
24
25
|
const isHorizontal = participantsBarPosition === 'top' || participantsBarPosition === 'bottom';
|
|
25
|
-
const isOneOnOneCall = otherParticipants.length === 1;
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
if (!participantsBarWrapperElement || !call)
|
|
28
28
|
return;
|
|
29
29
|
const cleanup = call.setViewport(participantsBarWrapperElement);
|
|
30
30
|
return () => cleanup();
|
|
31
31
|
}, [participantsBarWrapperElement, call]);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return;
|
|
35
|
-
// always show the remote participant in the spotlight
|
|
36
|
-
if (isOneOnOneCall) {
|
|
37
|
-
call.setSortParticipantsBy(combineComparators(screenSharing, loggedIn));
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
call.setSortParticipantsBy(speakerLayoutSortPreset);
|
|
41
|
-
}
|
|
42
|
-
return () => {
|
|
43
|
-
// reset the sorting to the default for the call type
|
|
44
|
-
const callConfig = CallTypes.get(call.type);
|
|
45
|
-
call.setSortParticipantsBy(callConfig.options.sortParticipantsBy || defaultSortPreset);
|
|
46
|
-
};
|
|
47
|
-
}, [call, isOneOnOneCall]);
|
|
32
|
+
const isOneOnOneCall = otherParticipants.length === 1;
|
|
33
|
+
useSpeakerLayoutSortPreset(call, isOneOnOneCall);
|
|
48
34
|
let participantsWithAppliedLimit = otherParticipants;
|
|
49
35
|
if (typeof participantsBarLimit !== 'undefined') {
|
|
50
36
|
const hardLimitToApply = isVertical
|
|
@@ -57,7 +43,7 @@ export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIB
|
|
|
57
43
|
}
|
|
58
44
|
if (!call)
|
|
59
45
|
return null;
|
|
60
|
-
return (_jsxs("div", Object.assign({ className: "str-video__speaker-layout__wrapper" }, { children: [
|
|
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 &&
|
|
61
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 }))] })))] }))] })));
|
|
62
48
|
};
|
|
63
49
|
const HorizontalScrollButtons = ({ scrollWrapper, }) => {
|
|
@@ -81,11 +67,4 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
|
|
|
81
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" }))] }));
|
|
82
68
|
};
|
|
83
69
|
const hasScreenShare = (p) => !!(p === null || p === void 0 ? void 0 : p.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE));
|
|
84
|
-
const loggedIn = (a, b) => {
|
|
85
|
-
if (a.isLocalParticipant)
|
|
86
|
-
return 1;
|
|
87
|
-
if (b.isLocalParticipant)
|
|
88
|
-
return -1;
|
|
89
|
-
return 0;
|
|
90
|
-
};
|
|
91
70
|
//# sourceMappingURL=SpeakerLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,
|
|
1
|
+
{"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":"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"}
|
|
@@ -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 +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"}
|
|
@@ -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.",
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.3.
|
|
1
|
+
export declare const version = "0.3.30";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '0.3.
|
|
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.
|
|
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.
|
|
37
|
-
"@stream-io/video-react-bindings": "^0.2.
|
|
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.
|
|
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
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
ParticipantView,
|
|
12
12
|
ParticipantViewProps,
|
|
13
13
|
} from '../ParticipantView';
|
|
14
|
-
import {
|
|
14
|
+
import { ParticipantsAudio } from '../Audio';
|
|
15
15
|
import { IconButton } from '../../../components';
|
|
16
16
|
import { chunk } from '../../../utilities';
|
|
17
|
+
import { usePaginatedLayoutSortPreset } from './hooks';
|
|
17
18
|
|
|
18
19
|
const GROUP_SIZE = 16;
|
|
19
20
|
|
|
@@ -85,6 +86,8 @@ export const PaginatedGridLayout = ({
|
|
|
85
86
|
// used to render audio elements
|
|
86
87
|
const remoteParticipants = useRemoteParticipants();
|
|
87
88
|
|
|
89
|
+
usePaginatedLayoutSortPreset(call);
|
|
90
|
+
|
|
88
91
|
// only used to render video elements
|
|
89
92
|
const participantGroups = useMemo(
|
|
90
93
|
() =>
|
|
@@ -110,9 +113,7 @@ export const PaginatedGridLayout = ({
|
|
|
110
113
|
|
|
111
114
|
return (
|
|
112
115
|
<div className="str-video__paginated-grid-layout__wrapper">
|
|
113
|
-
{remoteParticipants
|
|
114
|
-
<Audio key={participant.sessionId} participant={participant} />
|
|
115
|
-
))}
|
|
116
|
+
<ParticipantsAudio participants={remoteParticipants} />
|
|
116
117
|
<div className="str-video__paginated-grid-layout">
|
|
117
118
|
{pageArrowsVisible && pageCount > 1 && (
|
|
118
119
|
<IconButton
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
CallTypes,
|
|
4
|
-
combineComparators,
|
|
5
|
-
Comparator,
|
|
6
|
-
defaultSortPreset,
|
|
7
|
-
screenSharing,
|
|
8
|
-
SfuModels,
|
|
9
|
-
speakerLayoutSortPreset,
|
|
10
|
-
StreamVideoParticipant,
|
|
11
|
-
} from '@stream-io/video-client';
|
|
12
|
-
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
13
2
|
import clsx from 'clsx';
|
|
3
|
+
import { SfuModels, StreamVideoParticipant } from '@stream-io/video-client';
|
|
4
|
+
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
14
5
|
|
|
15
6
|
import {
|
|
16
7
|
DefaultParticipantViewUI,
|
|
@@ -22,14 +13,15 @@ import {
|
|
|
22
13
|
useHorizontalScrollPosition,
|
|
23
14
|
useVerticalScrollPosition,
|
|
24
15
|
} from '../../../hooks';
|
|
16
|
+
import { useSpeakerLayoutSortPreset } from './hooks';
|
|
25
17
|
import { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';
|
|
26
|
-
import {
|
|
18
|
+
import { ParticipantsAudio } from '../Audio';
|
|
27
19
|
|
|
28
20
|
export type SpeakerLayoutProps = {
|
|
29
21
|
ParticipantViewUISpotlight?: ParticipantViewProps['ParticipantViewUI'];
|
|
30
22
|
ParticipantViewUIBar?: ParticipantViewProps['ParticipantViewUI'];
|
|
31
23
|
/**
|
|
32
|
-
* The position of the
|
|
24
|
+
* The position of the participants who are not in focus.
|
|
33
25
|
* Providing `null` will hide the bar.
|
|
34
26
|
*/
|
|
35
27
|
participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;
|
|
@@ -75,7 +67,6 @@ export const SpeakerLayout = ({
|
|
|
75
67
|
participantsBarPosition === 'left' || participantsBarPosition === 'right';
|
|
76
68
|
const isHorizontal =
|
|
77
69
|
participantsBarPosition === 'top' || participantsBarPosition === 'bottom';
|
|
78
|
-
const isOneOnOneCall = otherParticipants.length === 1;
|
|
79
70
|
|
|
80
71
|
useEffect(() => {
|
|
81
72
|
if (!participantsBarWrapperElement || !call) return;
|
|
@@ -84,23 +75,8 @@ export const SpeakerLayout = ({
|
|
|
84
75
|
return () => cleanup();
|
|
85
76
|
}, [participantsBarWrapperElement, call]);
|
|
86
77
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// always show the remote participant in the spotlight
|
|
90
|
-
if (isOneOnOneCall) {
|
|
91
|
-
call.setSortParticipantsBy(combineComparators(screenSharing, loggedIn));
|
|
92
|
-
} else {
|
|
93
|
-
call.setSortParticipantsBy(speakerLayoutSortPreset);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return () => {
|
|
97
|
-
// reset the sorting to the default for the call type
|
|
98
|
-
const callConfig = CallTypes.get(call.type);
|
|
99
|
-
call.setSortParticipantsBy(
|
|
100
|
-
callConfig.options.sortParticipantsBy || defaultSortPreset,
|
|
101
|
-
);
|
|
102
|
-
};
|
|
103
|
-
}, [call, isOneOnOneCall]);
|
|
78
|
+
const isOneOnOneCall = otherParticipants.length === 1;
|
|
79
|
+
useSpeakerLayoutSortPreset(call, isOneOnOneCall);
|
|
104
80
|
|
|
105
81
|
let participantsWithAppliedLimit = otherParticipants;
|
|
106
82
|
|
|
@@ -121,9 +97,7 @@ export const SpeakerLayout = ({
|
|
|
121
97
|
|
|
122
98
|
return (
|
|
123
99
|
<div className="str-video__speaker-layout__wrapper">
|
|
124
|
-
{remoteParticipants
|
|
125
|
-
<Audio key={participant.sessionId} participant={participant} />
|
|
126
|
-
))}
|
|
100
|
+
<ParticipantsAudio participants={remoteParticipants} />
|
|
127
101
|
<div
|
|
128
102
|
className={clsx(
|
|
129
103
|
'str-video__speaker-layout',
|
|
@@ -272,9 +246,3 @@ const VerticalScrollButtons = <T extends HTMLElement>({
|
|
|
272
246
|
|
|
273
247
|
const hasScreenShare = (p?: StreamVideoParticipant) =>
|
|
274
248
|
!!p?.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE);
|
|
275
|
-
|
|
276
|
-
const loggedIn: Comparator<StreamVideoParticipant> = (a, b) => {
|
|
277
|
-
if (a.isLocalParticipant) return 1;
|
|
278
|
-
if (b.isLocalParticipant) return -1;
|
|
279
|
-
return 0;
|
|
280
|
-
};
|