@stream-io/video-react-sdk 0.0.34 → 0.0.36
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 +14 -0
- package/README.md +80 -3
- package/dist/src/core/components/CallLayout/SpeakerLayout.js +1 -1
- package/dist/src/core/components/ParticipantView/DefaultParticipantViewUI.js +2 -2
- package/dist/src/core/components/ParticipantView/ParticipantView.d.ts +7 -7
- package/dist/src/core/components/ParticipantView/ParticipantView.js +4 -4
- package/dist/src/core/components/Video/Video.d.ts +1 -1
- package/dist/src/core/components/Video/Video.js +11 -5
- package/dist/src/core/components/Video/Video.js.map +1 -1
- package/package.json +1 -1
- package/src/core/components/CallLayout/SpeakerLayout.tsx +1 -1
- package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +2 -2
- package/src/core/components/ParticipantView/ParticipantView.tsx +8 -8
- package/src/core/components/Video/Video.tsx +16 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [0.0.36](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-0.0.35...@stream-io/video-react-sdk-0.0.36) (2023-06-19)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **react-sdk:** vale lint issue in README.md ([#665](https://github.com/GetStream/stream-video-js/issues/665)) ([f21fe8e](https://github.com/GetStream/stream-video-js/commit/f21fe8e74302f3f3b436f3f1bf0f64335d9c936a))
|
|
11
|
+
|
|
12
|
+
### [0.0.35](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-0.0.34...@stream-io/video-react-sdk-0.0.35) (2023-06-16)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* Rename videoKind prop to videoMode ([#661](https://github.com/GetStream/stream-video-js/issues/661)) ([781e908](https://github.com/GetStream/stream-video-js/commit/781e9081fd43f2a433f9c4c7b32a549d77fb26c1))
|
|
18
|
+
|
|
5
19
|
### [0.0.34](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-0.0.33...@stream-io/video-react-sdk-0.0.34) (2023-06-16)
|
|
6
20
|
|
|
7
21
|
### Dependency Updates
|
package/README.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Official React SDK for [Stream Video](https://getstream.io/video/docs/)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## **Quick Links**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- [Register](https://getstream.io/chat/trial/) to get an API key for Stream Video
|
|
6
|
+
- [React Video Tutorial](/sample-apps/react/stream-video-react-tutorial)
|
|
7
|
+
- [Sample application](../../sample-apps/react/stream-video-react-tutorial)
|
|
8
|
+
|
|
9
|
+
## What is Stream?
|
|
10
|
+
|
|
11
|
+
Stream allows developers to rapidly deploy scalable feeds, chat messaging and video with an industry leading 99.999% uptime SLA guarantee.
|
|
12
|
+
|
|
13
|
+
With Stream's video components, you can use their SDK to build in-app video calling, audio rooms, audio calls, or live streaming. The best place to get started is with their tutorials:
|
|
14
|
+
|
|
15
|
+
- Video & Audio Calling Tutorial
|
|
16
|
+
- Audio Rooms Tutorial
|
|
17
|
+
- Livestreaming Tutorial
|
|
18
|
+
|
|
19
|
+
Stream provides UI components and state handling that make it easy to build video calling for your app. All calls run on Stream's network of edge servers around the world, ensuring optimal latency and reliability.
|
|
20
|
+
|
|
21
|
+
## 👩💻 Free for Makers 👨💻
|
|
22
|
+
|
|
23
|
+
Stream is free for most side and hobby projects. To qualify, your project/company needs to have < 5 team members and < $10k in monthly revenue. Makers get $100 in monthly credit for video for free.
|
|
24
|
+
|
|
25
|
+
## 💡Supported Features💡
|
|
26
|
+
|
|
27
|
+
Here are some of the features we support:
|
|
28
|
+
|
|
29
|
+
- Developer experience: Great SDKs, docs, tutorials and support so you can build quickly
|
|
30
|
+
- Edge network: Servers around the world ensure optimal latency and reliability
|
|
31
|
+
- Chat: Stored chat, reactions, threads, typing indicators, URL previews etc
|
|
32
|
+
- Security & Privacy: Based in USA and EU, Soc2 certified, GDPR compliant
|
|
33
|
+
- Dynascale: Automatically switch resolutions, fps, bitrate, codecs and paginate video on large calls
|
|
34
|
+
- Screen sharing
|
|
35
|
+
- Picture in picture support
|
|
36
|
+
- Active speaker
|
|
37
|
+
- Custom events
|
|
38
|
+
- Geofencing
|
|
39
|
+
- Notifications and ringing calls
|
|
40
|
+
- Opus DTX & Red for reliable audio
|
|
41
|
+
- Webhooks & SQS
|
|
42
|
+
- Backstage mode
|
|
43
|
+
- Flexible permissions system
|
|
44
|
+
- Joining calls by ID, link or invite
|
|
45
|
+
- Enabling and disabling audio and video when in calls
|
|
46
|
+
- Flipping, Enabling and disabling camera in calls
|
|
47
|
+
- Enabling and disabling speakerphone in calls
|
|
48
|
+
- Push notification providers support
|
|
49
|
+
- Call recording
|
|
50
|
+
- Broadcasting to HLS
|
|
51
|
+
|
|
52
|
+
## Roadmap
|
|
53
|
+
|
|
54
|
+
### 0.1 milestone
|
|
55
|
+
- [ ] Call session migration
|
|
56
|
+
- [ ] Hardware-accelerated video encoding on supported platforms
|
|
57
|
+
|
|
58
|
+
### 0.2 milestone
|
|
59
|
+
- [ ] Composite layout for streaming and recording
|
|
60
|
+
- [ ] Dynascale: turn off incoming video when the browser is in the background
|
|
61
|
+
- [ ] Performance and bundle-size optimizations
|
|
62
|
+
- [ ] Typescript generics enhancements
|
|
63
|
+
- [ ] Logging 2.0
|
|
64
|
+
- [ ] E2E testing platform
|
|
65
|
+
- [ ] Increase test coverage
|
|
66
|
+
|
|
67
|
+
### 0.3 milestone
|
|
68
|
+
- [ ] Video and audio filters
|
|
69
|
+
- [ ] Transcriptions
|
|
70
|
+
- [ ] Closed captions
|
|
71
|
+
|
|
72
|
+
### 0.4 milestone
|
|
73
|
+
- [ ] Enhanced UI components and theming
|
|
74
|
+
- [ ] Break-out rooms
|
|
75
|
+
- [ ] Waiting rooms
|
|
76
|
+
|
|
77
|
+
## Contributing
|
|
78
|
+
|
|
79
|
+
- How can I submit a sample app?
|
|
80
|
+
- Apps submissions are always welcome. 🥳 Open a PR with a proper description and we'll review it as soon as possible.
|
|
81
|
+
- Spot a bug 🕷 ?
|
|
82
|
+
- We welcome code changes that improve the apps or fix a problem. Please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github.
|
|
@@ -39,7 +39,7 @@ export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIB
|
|
|
39
39
|
return null;
|
|
40
40
|
const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
|
|
41
41
|
return (_jsx("div", Object.assign({ className: "str-video__speaker-layout__wrapper" }, { children: _jsxs("div", Object.assign({ className: clsx('str-video__speaker-layout', participantsBarPosition &&
|
|
42
|
-
`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,
|
|
42
|
+
`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, videoMode: isSpeakerScreenSharing ? 'screen' : 'video', 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' ||
|
|
43
43
|
participantsBarPosition === 'right') && (_jsx(VerticalScrollButtons, { scrollWrapper: scrollWrapper })), (participantsBarPosition === 'top' ||
|
|
44
44
|
participantsBarPosition === 'bottom') && (_jsx(HorizontalScrollButtons, { scrollWrapper: scrollWrapper }))] })))] })) })));
|
|
45
45
|
};
|
|
@@ -21,12 +21,12 @@ export const DefaultScreenShareOverlay = () => {
|
|
|
21
21
|
};
|
|
22
22
|
export const DefaultParticipantViewUI = ({ indicatorsVisible = true, menuPlacement = 'bottom-end', showMenuButton = true, }) => {
|
|
23
23
|
const call = useCall();
|
|
24
|
-
const { participant, participantViewElement,
|
|
24
|
+
const { participant, participantViewElement, videoMode, videoElement } = useParticipantViewContext();
|
|
25
25
|
const { reaction, sessionId, publishedTracks } = participant;
|
|
26
26
|
const hasScreenShare = publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE);
|
|
27
27
|
if (participant.isLocalParticipant &&
|
|
28
28
|
hasScreenShare &&
|
|
29
|
-
|
|
29
|
+
videoMode === 'screen')
|
|
30
30
|
return (_jsxs(_Fragment, { children: [_jsx(DefaultScreenShareOverlay, {}), _jsx(ParticipantDetails, { indicatorsVisible: indicatorsVisible })] }));
|
|
31
31
|
return (_jsxs(_Fragment, { children: [showMenuButton && (_jsx(MenuToggle, Object.assign({ strategy: "fixed", placement: menuPlacement, ToggleButton: ToggleButton }, { children: _jsx(ParticipantActionsContextMenu, { participantViewElement: participantViewElement, participant: participant, videoElement: videoElement }) }))), reaction && (_jsx(Reaction, { reaction: reaction, sessionId: sessionId, call: call })), _jsx(ParticipantDetails, { indicatorsVisible: indicatorsVisible })] }));
|
|
32
32
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentType, ReactElement } from 'react';
|
|
2
2
|
import { StreamVideoLocalParticipant, StreamVideoParticipant } from '@stream-io/video-client';
|
|
3
3
|
import { VideoProps } from '../Video';
|
|
4
|
-
export type ParticipantViewContextValue = Required<Pick<ParticipantViewProps, 'participant' | '
|
|
4
|
+
export type ParticipantViewContextValue = Required<Pick<ParticipantViewProps, 'participant' | 'videoMode'>> & {
|
|
5
5
|
participantViewElement: HTMLDivElement | null;
|
|
6
6
|
videoElement: HTMLVideoElement | null;
|
|
7
7
|
videoPlaceholderElement: HTMLDivElement | null;
|
|
@@ -19,11 +19,11 @@ export type ParticipantViewProps = {
|
|
|
19
19
|
*/
|
|
20
20
|
ParticipantViewUI?: ComponentType | ReactElement | null;
|
|
21
21
|
/**
|
|
22
|
-
* The kind of video stream to play for the given participant.
|
|
22
|
+
* The kind of video stream to play for the given participant. The default value is `video`. You can use `none` if you're building an audio-only call.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
videoMode?: 'video' | 'screen' | 'none';
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* This prop is only useful for advanced use-cases (for example building your own paginated layout). When set to `true` it will mute the give participant's audio stream on the client side. The local participant is always muted.
|
|
27
27
|
*/
|
|
28
28
|
muteAudio?: boolean;
|
|
29
29
|
/**
|
|
@@ -50,11 +50,11 @@ export declare const ParticipantView: import("react").ForwardRefExoticComponent<
|
|
|
50
50
|
*/
|
|
51
51
|
ParticipantViewUI?: ReactElement<any, string | import("react").JSXElementConstructor<any>> | ComponentType<{}> | null | undefined;
|
|
52
52
|
/**
|
|
53
|
-
* The kind of video stream to play for the given participant.
|
|
53
|
+
* The kind of video stream to play for the given participant. The default value is `video`. You can use `none` if you're building an audio-only call.
|
|
54
54
|
*/
|
|
55
|
-
|
|
55
|
+
videoMode?: "video" | "none" | "screen" | undefined;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* This prop is only useful for advanced use-cases (for example building your own paginated layout). When set to `true` it will mute the give participant's audio stream on the client side. The local participant is always muted.
|
|
58
58
|
*/
|
|
59
59
|
muteAudio?: boolean | undefined;
|
|
60
60
|
/**
|
|
@@ -11,7 +11,7 @@ import { isComponentType, applyElementToRef } from '../../../utilities';
|
|
|
11
11
|
import { useLocalParticipant } from '@stream-io/video-react-bindings';
|
|
12
12
|
const ParticipantViewContext = createContext(undefined);
|
|
13
13
|
export const useParticipantViewContext = () => useContext(ParticipantViewContext);
|
|
14
|
-
export const ParticipantView = forwardRef(({ participant,
|
|
14
|
+
export const ParticipantView = forwardRef(({ participant, videoMode = 'video', muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) => {
|
|
15
15
|
const { audioStream, isLocalParticipant, isSpeaking, publishedTracks, sessionId, } = participant;
|
|
16
16
|
const localParticipant = useLocalParticipant();
|
|
17
17
|
const hasAudio = publishedTracks.includes(SfuModels.TrackType.AUDIO);
|
|
@@ -29,13 +29,13 @@ export const ParticipantView = forwardRef(({ participant, videoKind = 'video', m
|
|
|
29
29
|
participantViewElement: trackedElement,
|
|
30
30
|
videoElement: contextVideoElement,
|
|
31
31
|
videoPlaceholderElement: contextVideoPlaceholderElement,
|
|
32
|
-
|
|
32
|
+
videoMode,
|
|
33
33
|
}), [
|
|
34
34
|
contextVideoElement,
|
|
35
35
|
contextVideoPlaceholderElement,
|
|
36
36
|
participant,
|
|
37
37
|
trackedElement,
|
|
38
|
-
|
|
38
|
+
videoMode,
|
|
39
39
|
]);
|
|
40
40
|
const videoRefs = useMemo(() => ({
|
|
41
41
|
setVideoElement: (element) => {
|
|
@@ -54,6 +54,6 @@ export const ParticipantView = forwardRef(({ participant, videoKind = 'video', m
|
|
|
54
54
|
// mute the local participant, as we don't want to hear ourselves
|
|
55
55
|
, {
|
|
56
56
|
// mute the local participant, as we don't want to hear ourselves
|
|
57
|
-
muted: isLocalParticipant || muteAudio, sinkId: localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.audioOutputDeviceId, audioStream: audioStream }), _jsx(Video, { VideoPlaceholder: VideoPlaceholder, participant: participant, kind:
|
|
57
|
+
muted: isLocalParticipant || muteAudio, sinkId: localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.audioOutputDeviceId, audioStream: audioStream }), _jsx(Video, { VideoPlaceholder: VideoPlaceholder, participant: participant, kind: videoMode, refs: videoRefs, autoPlay: true }), isComponentType(ParticipantViewUI) ? (_jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] })) })));
|
|
58
58
|
});
|
|
59
59
|
//# sourceMappingURL=ParticipantView.js.map
|
|
@@ -2,7 +2,7 @@ import { ComponentPropsWithoutRef, ComponentType } from 'react';
|
|
|
2
2
|
import { StreamVideoParticipant } from '@stream-io/video-client';
|
|
3
3
|
import { VideoPlaceholderProps } from './DefaultVideoPlaceholder';
|
|
4
4
|
export type VideoProps = ComponentPropsWithoutRef<'video'> & {
|
|
5
|
-
kind: 'video' | 'screen';
|
|
5
|
+
kind: 'video' | 'screen' | 'none';
|
|
6
6
|
participant: StreamVideoParticipant;
|
|
7
7
|
/**
|
|
8
8
|
* Override the default UI that's visible when a participant turned off their video.
|
|
@@ -24,7 +24,11 @@ export const Video = (_a) => {
|
|
|
24
24
|
// const [videoTrackMuted, setVideoTrackMuted] = useState(false);
|
|
25
25
|
const [videoPlaying, setVideoPlaying] = useState(false);
|
|
26
26
|
const viewportVisibilityRef = useRef(viewportVisibilityState);
|
|
27
|
-
const stream = kind === '
|
|
27
|
+
const stream = kind === 'none'
|
|
28
|
+
? undefined
|
|
29
|
+
: kind === 'video'
|
|
30
|
+
? videoStream
|
|
31
|
+
: screenShareStream;
|
|
28
32
|
// TODO: handle track muting
|
|
29
33
|
// useEffect(() => {
|
|
30
34
|
// if (!stream) return;
|
|
@@ -43,16 +47,18 @@ export const Video = (_a) => {
|
|
|
43
47
|
// track.removeEventListener('unmute', handleUnmute);
|
|
44
48
|
// };
|
|
45
49
|
// }, [stream]);
|
|
46
|
-
const isPublishingTrack =
|
|
47
|
-
?
|
|
48
|
-
:
|
|
50
|
+
const isPublishingTrack = kind === 'none'
|
|
51
|
+
? false
|
|
52
|
+
: publishedTracks.includes(kind === 'video'
|
|
53
|
+
? SfuModels.TrackType.VIDEO
|
|
54
|
+
: SfuModels.TrackType.SCREEN_SHARE);
|
|
49
55
|
const displayPlaceholder = !isPublishingTrack ||
|
|
50
56
|
(viewportVisibilityState === VisibilityState.INVISIBLE &&
|
|
51
57
|
!screenShareStream) ||
|
|
52
58
|
!videoPlaying;
|
|
53
59
|
const lastDimensionRef = useRef();
|
|
54
60
|
const updateSubscription = useCallback((dimension, type = DebounceType.SLOW) => {
|
|
55
|
-
if (!call)
|
|
61
|
+
if (!call || kind === 'none')
|
|
56
62
|
return;
|
|
57
63
|
call.updateSubscriptionsPartial(kind, {
|
|
58
64
|
[sessionId]: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Video.js","sourceRoot":"","sources":["../../../../../src/core/components/Video/Video.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAGL,WAAW,EACX,SAAS,EACT,MAAM,EACN,QAAQ,GACT,MAAM,OAAO,CAAC;AACf,OAAO,EACL,YAAY,EACZ,SAAS,EAET,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EACL,uBAAuB,GAExB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAe1D,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAOT,EAAE,EAAE;QAPK,EACpB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,gBAAgB,GAAG,uBAAuB,EAC1C,IAAI,OAEO,EADR,IAAI,cANa,gEAOrB,CADQ;IAEP,MAAM,EACJ,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,MAAM,GACP,GAAG,WAAW,CAAC;IAEhB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAC9C,IAAI,CACL,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,qBAAqB,GAAG,MAAM,CAClC,uBAAuB,CACxB,CAAC;IAEF,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"Video.js","sourceRoot":"","sources":["../../../../../src/core/components/Video/Video.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -96,7 +96,7 @@ export const SpeakerLayout = ({
|
|
|
96
96
|
<ParticipantView
|
|
97
97
|
participant={participantInSpotlight}
|
|
98
98
|
muteAudio={isSpeakerScreenSharing}
|
|
99
|
-
|
|
99
|
+
videoMode={isSpeakerScreenSharing ? 'screen' : 'video'}
|
|
100
100
|
ParticipantViewUI={ParticipantViewUISpotlight}
|
|
101
101
|
VideoPlaceholder={VideoPlaceholder}
|
|
102
102
|
/>
|
|
@@ -69,7 +69,7 @@ export const DefaultParticipantViewUI = ({
|
|
|
69
69
|
showMenuButton = true,
|
|
70
70
|
}: DefaultParticipantViewUIProps) => {
|
|
71
71
|
const call = useCall()!;
|
|
72
|
-
const { participant, participantViewElement,
|
|
72
|
+
const { participant, participantViewElement, videoMode, videoElement } =
|
|
73
73
|
useParticipantViewContext();
|
|
74
74
|
const { reaction, sessionId, publishedTracks } = participant;
|
|
75
75
|
|
|
@@ -80,7 +80,7 @@ export const DefaultParticipantViewUI = ({
|
|
|
80
80
|
if (
|
|
81
81
|
participant.isLocalParticipant &&
|
|
82
82
|
hasScreenShare &&
|
|
83
|
-
|
|
83
|
+
videoMode === 'screen'
|
|
84
84
|
)
|
|
85
85
|
return (
|
|
86
86
|
<>
|
|
@@ -21,7 +21,7 @@ import { isComponentType, applyElementToRef } from '../../../utilities';
|
|
|
21
21
|
import { useLocalParticipant } from '@stream-io/video-react-bindings';
|
|
22
22
|
|
|
23
23
|
export type ParticipantViewContextValue = Required<
|
|
24
|
-
Pick<ParticipantViewProps, 'participant' | '
|
|
24
|
+
Pick<ParticipantViewProps, 'participant' | 'videoMode'>
|
|
25
25
|
> & {
|
|
26
26
|
participantViewElement: HTMLDivElement | null;
|
|
27
27
|
videoElement: HTMLVideoElement | null;
|
|
@@ -49,12 +49,12 @@ export type ParticipantViewProps = {
|
|
|
49
49
|
ParticipantViewUI?: ComponentType | ReactElement | null;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* The kind of video stream to play for the given participant.
|
|
52
|
+
* The kind of video stream to play for the given participant. The default value is `video`. You can use `none` if you're building an audio-only call.
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
videoMode?: 'video' | 'screen' | 'none';
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* This prop is only useful for advanced use-cases (for example building your own paginated layout). When set to `true` it will mute the give participant's audio stream on the client side. The local participant is always muted.
|
|
58
58
|
*/
|
|
59
59
|
muteAudio?: boolean;
|
|
60
60
|
|
|
@@ -76,7 +76,7 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
|
|
|
76
76
|
(
|
|
77
77
|
{
|
|
78
78
|
participant,
|
|
79
|
-
|
|
79
|
+
videoMode = 'video',
|
|
80
80
|
muteAudio,
|
|
81
81
|
refs: { setVideoElement, setVideoPlaceholderElement } = {},
|
|
82
82
|
className,
|
|
@@ -119,14 +119,14 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
|
|
|
119
119
|
participantViewElement: trackedElement,
|
|
120
120
|
videoElement: contextVideoElement,
|
|
121
121
|
videoPlaceholderElement: contextVideoPlaceholderElement,
|
|
122
|
-
|
|
122
|
+
videoMode,
|
|
123
123
|
}),
|
|
124
124
|
[
|
|
125
125
|
contextVideoElement,
|
|
126
126
|
contextVideoPlaceholderElement,
|
|
127
127
|
participant,
|
|
128
128
|
trackedElement,
|
|
129
|
-
|
|
129
|
+
videoMode,
|
|
130
130
|
],
|
|
131
131
|
);
|
|
132
132
|
|
|
@@ -168,7 +168,7 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
|
|
|
168
168
|
<Video
|
|
169
169
|
VideoPlaceholder={VideoPlaceholder}
|
|
170
170
|
participant={participant}
|
|
171
|
-
kind={
|
|
171
|
+
kind={videoMode}
|
|
172
172
|
refs={videoRefs}
|
|
173
173
|
autoPlay
|
|
174
174
|
/>
|
|
@@ -21,7 +21,7 @@ import { BaseVideo } from './BaseVideo';
|
|
|
21
21
|
import { useCall } from '@stream-io/video-react-bindings';
|
|
22
22
|
|
|
23
23
|
export type VideoProps = ComponentPropsWithoutRef<'video'> & {
|
|
24
|
-
kind: 'video' | 'screen';
|
|
24
|
+
kind: 'video' | 'screen' | 'none';
|
|
25
25
|
participant: StreamVideoParticipant;
|
|
26
26
|
/**
|
|
27
27
|
* Override the default UI that's visible when a participant turned off their video.
|
|
@@ -63,7 +63,12 @@ export const Video = ({
|
|
|
63
63
|
viewportVisibilityState,
|
|
64
64
|
);
|
|
65
65
|
|
|
66
|
-
const stream =
|
|
66
|
+
const stream =
|
|
67
|
+
kind === 'none'
|
|
68
|
+
? undefined
|
|
69
|
+
: kind === 'video'
|
|
70
|
+
? videoStream
|
|
71
|
+
: screenShareStream;
|
|
67
72
|
|
|
68
73
|
// TODO: handle track muting
|
|
69
74
|
// useEffect(() => {
|
|
@@ -88,11 +93,14 @@ export const Video = ({
|
|
|
88
93
|
// };
|
|
89
94
|
// }, [stream]);
|
|
90
95
|
|
|
91
|
-
const isPublishingTrack =
|
|
92
|
-
kind === '
|
|
93
|
-
?
|
|
94
|
-
:
|
|
95
|
-
|
|
96
|
+
const isPublishingTrack =
|
|
97
|
+
kind === 'none'
|
|
98
|
+
? false
|
|
99
|
+
: publishedTracks.includes(
|
|
100
|
+
kind === 'video'
|
|
101
|
+
? SfuModels.TrackType.VIDEO
|
|
102
|
+
: SfuModels.TrackType.SCREEN_SHARE,
|
|
103
|
+
);
|
|
96
104
|
|
|
97
105
|
const displayPlaceholder =
|
|
98
106
|
!isPublishingTrack ||
|
|
@@ -106,7 +114,7 @@ export const Video = ({
|
|
|
106
114
|
dimension?: SfuModels.VideoDimension,
|
|
107
115
|
type: DebounceType = DebounceType.SLOW,
|
|
108
116
|
) => {
|
|
109
|
-
if (!call) return;
|
|
117
|
+
if (!call || kind === 'none') return;
|
|
110
118
|
|
|
111
119
|
call.updateSubscriptionsPartial(
|
|
112
120
|
kind,
|