@stream-io/video-react-sdk 1.4.5 → 1.6.0

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.
@@ -2,7 +2,7 @@ import { ReactElement } from 'react';
2
2
  export type DropDownSelectOptionProps = {
3
3
  label: string;
4
4
  selected?: boolean;
5
- icon: string;
5
+ icon?: string;
6
6
  };
7
7
  export declare const DropDownSelectOption: (props: DropDownSelectOptionProps) => import("react/jsx-runtime").JSX.Element;
8
8
  export declare const DropDownSelect: (props: {
@@ -26,6 +26,11 @@ export type LivestreamLayoutProps = {
26
26
  * Whether to show the speaker name. Defaults to `false`.
27
27
  */
28
28
  showSpeakerName?: boolean;
29
+ /**
30
+ * When set to `false` disables mirroring of the local partipant's video.
31
+ * @default true
32
+ */
33
+ mirrorLocalParticipantVideo?: boolean;
29
34
  /**
30
35
  * The props to pass to the floating participant element.
31
36
  */
@@ -9,6 +9,11 @@ export type PaginatedGridLayoutProps = {
9
9
  * @default false
10
10
  */
11
11
  excludeLocalParticipant?: boolean;
12
+ /**
13
+ * When set to `false` disables mirroring of the local partipant's video.
14
+ * @default true
15
+ */
16
+ mirrorLocalParticipantVideo?: boolean;
12
17
  /**
13
18
  * Turns on/off the pagination arrows.
14
19
  * @default true
@@ -23,6 +23,11 @@ export type SpeakerLayoutProps = {
23
23
  * @default false
24
24
  */
25
25
  excludeLocalParticipant?: boolean;
26
+ /**
27
+ * When set to `false` disables mirroring of the local partipant's video.
28
+ * @default true
29
+ */
30
+ mirrorLocalParticipantVideo?: boolean;
26
31
  /**
27
32
  * Turns on/off the pagination arrows.
28
33
  * @default true
@@ -30,6 +35,6 @@ export type SpeakerLayoutProps = {
30
35
  pageArrowsVisible?: boolean;
31
36
  } & Pick<ParticipantViewProps, 'VideoPlaceholder'>;
32
37
  export declare const SpeakerLayout: {
33
- ({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition, participantsBarLimit, excludeLocalParticipant, pageArrowsVisible, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
38
+ ({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition, participantsBarLimit, mirrorLocalParticipantVideo, excludeLocalParticipant, pageArrowsVisible, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
34
39
  displayName: string;
35
40
  };
@@ -18,6 +18,11 @@ export type ParticipantViewProps = {
18
18
  * You can use `none` if you're building an audio-only call.
19
19
  */
20
20
  trackType?: VideoTrackType | 'none';
21
+ /**
22
+ * Forces participant's video to be mirrored or unmirrored. By default, video track
23
+ * from the local participant is mirrored, and all other videos are not mirrored.
24
+ */
25
+ mirror?: boolean;
21
26
  /**
22
27
  * This prop is only useful for advanced use-cases (for example, building your own layout).
23
28
  * When set to `true` it will mute the give participant's audio stream on the client side.
@@ -54,6 +59,11 @@ export declare const ParticipantView: import("react").ForwardRefExoticComponent<
54
59
  * You can use `none` if you're building an audio-only call.
55
60
  */
56
61
  trackType?: VideoTrackType | "none";
62
+ /**
63
+ * Forces participant's video to be mirrored or unmirrored. By default, video track
64
+ * from the local participant is mirrored, and all other videos are not mirrored.
65
+ */
66
+ mirror?: boolean;
57
67
  /**
58
68
  * This prop is only useful for advanced use-cases (for example, building your own layout).
59
69
  * When set to `true` it will mute the give participant's audio stream on the client side.
@@ -2,6 +2,16 @@ import { ComponentPropsWithoutRef, ComponentType } from 'react';
2
2
  import { StreamVideoParticipant, VideoTrackType } from '@stream-io/video-client';
3
3
  import { VideoPlaceholderProps } from './DefaultVideoPlaceholder';
4
4
  export type VideoProps = ComponentPropsWithoutRef<'video'> & {
5
+ /**
6
+ * Pass false to disable rendering video and render fallback
7
+ * even if the participant has published video.
8
+ */
9
+ enabled?: boolean;
10
+ /**
11
+ * Forces the video to be mirrored or unmirrored. By default, video track
12
+ * from the local participant is mirrored, and all other videos are not mirrored.
13
+ */
14
+ mirror?: boolean;
5
15
  /**
6
16
  * The track type to display.
7
17
  */
@@ -35,6 +45,6 @@ export type VideoProps = ComponentPropsWithoutRef<'video'> & {
35
45
  };
36
46
  };
37
47
  export declare const Video: {
38
- ({ trackType, participant, className, VideoPlaceholder, refs, ...rest }: VideoProps): import("react/jsx-runtime").JSX.Element | null;
48
+ ({ enabled, mirror, trackType, participant, className, VideoPlaceholder, refs, ...rest }: VideoProps): import("react/jsx-runtime").JSX.Element | null;
39
49
  displayName: string;
40
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "1.4.5",
3
+ "version": "1.6.0",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.es.js",
@@ -32,9 +32,9 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "@floating-ui/react": "^0.26.24",
35
- "@stream-io/video-client": "1.7.4",
35
+ "@stream-io/video-client": "1.8.0",
36
36
  "@stream-io/video-filters-web": "0.1.4",
37
- "@stream-io/video-react-bindings": "1.0.10",
37
+ "@stream-io/video-react-bindings": "1.1.0",
38
38
  "chart.js": "^4.4.4",
39
39
  "clsx": "^2.0.0",
40
40
  "react-chartjs-2": "^5.2.0"
@@ -48,7 +48,7 @@
48
48
  "@rollup/plugin-replace": "^5.0.7",
49
49
  "@rollup/plugin-typescript": "^11.1.6",
50
50
  "@stream-io/audio-filters-web": "^0.2.2",
51
- "@stream-io/video-styling": "^1.0.6",
51
+ "@stream-io/video-styling": "^1.1.0",
52
52
  "@types/react": "^18.3.2",
53
53
  "@types/react-dom": "^18.3.0",
54
54
  "react": "^18.3.1",
@@ -31,6 +31,7 @@ export const CompositeButton = forwardRef<
31
31
  IconButtonWithMenuProps
32
32
  >(function CompositeButton(
33
33
  {
34
+ disabled,
34
35
  caption,
35
36
  children,
36
37
  className,
@@ -63,6 +64,7 @@ export const CompositeButton = forwardRef<
63
64
  active && variant === 'primary',
64
65
  'str-video__composite-button__button-group--active-secondary':
65
66
  active && variant === 'secondary',
67
+ 'str-video__composite-button__button-group--disabled': disabled,
66
68
  })}
67
69
  >
68
70
  <button
@@ -72,6 +74,7 @@ export const CompositeButton = forwardRef<
72
74
  e.preventDefault();
73
75
  onClick?.(e);
74
76
  }}
77
+ disabled={disabled}
75
78
  {...restButtonProps}
76
79
  >
77
80
  {children}
@@ -11,10 +11,8 @@ import { IconButton } from '../Button';
11
11
  import { MenuToggle, ToggleMenuButtonProps } from '../Menu';
12
12
  import { WithTooltip } from '../Tooltip';
13
13
  import { Avatar } from '../Avatar';
14
- import {
15
- ParticipantActionsContextMenu,
16
- ParticipantViewContext,
17
- } from '../../core/';
14
+ import { ParticipantActionsContextMenu } from '../../core/components/ParticipantView/ParticipantActionsContextMenu';
15
+ import { ParticipantViewContext } from '../../core/components/ParticipantView/ParticipantViewContext';
18
16
 
19
17
  type CallParticipantListingItemProps = {
20
18
  /** Participant object be rendered */
@@ -162,7 +162,7 @@ const Select = (props: {
162
162
  export type DropDownSelectOptionProps = {
163
163
  label: string;
164
164
  selected?: boolean;
165
- icon: string;
165
+ icon?: string;
166
166
  };
167
167
 
168
168
  export const DropDownSelectOption = (props: DropDownSelectOptionProps) => {
@@ -179,7 +179,7 @@ export const DropDownSelectOption = (props: DropDownSelectOptionProps) => {
179
179
  onClick: () => handleSelect(index),
180
180
  })}
181
181
  >
182
- <Icon className="str-video__dropdown-icon" icon={icon} />
182
+ {icon && <Icon className="str-video__dropdown-icon" icon={icon} />}
183
183
  <span className="str-video__dropdown-label">{label}</span>
184
184
  </div>
185
185
  );
@@ -44,6 +44,12 @@ export type LivestreamLayoutProps = {
44
44
  */
45
45
  showSpeakerName?: boolean;
46
46
 
47
+ /**
48
+ * When set to `false` disables mirroring of the local partipant's video.
49
+ * @default true
50
+ */
51
+ mirrorLocalParticipantVideo?: boolean;
52
+
47
53
  /**
48
54
  * The props to pass to the floating participant element.
49
55
  */
@@ -116,6 +122,9 @@ export const LivestreamLayout = (props: LivestreamLayoutProps) => {
116
122
  )}
117
123
  participant={currentSpeaker}
118
124
  ParticipantViewUI={FloatingParticipantOverlay || Overlay}
125
+ mirror={
126
+ props.mirrorLocalParticipantVideo !== false ? undefined : false
127
+ }
119
128
  muteAudio // audio is rendered by ParticipantsAudio
120
129
  />
121
130
  )}
@@ -20,11 +20,12 @@ type PaginatedGridLayoutGroupProps = {
20
20
  * The group of participants to render.
21
21
  */
22
22
  group: Array<StreamVideoParticipant>;
23
- } & Pick<ParticipantViewProps, 'VideoPlaceholder'> &
23
+ } & Pick<ParticipantViewProps, 'VideoPlaceholder' | 'mirror'> &
24
24
  Required<Pick<ParticipantViewProps, 'ParticipantViewUI'>>;
25
25
 
26
26
  const PaginatedGridLayoutGroup = ({
27
27
  group,
28
+ mirror,
28
29
  VideoPlaceholder,
29
30
  ParticipantViewUI,
30
31
  }: PaginatedGridLayoutGroupProps) => {
@@ -43,6 +44,7 @@ const PaginatedGridLayoutGroup = ({
43
44
  key={participant.sessionId}
44
45
  participant={participant}
45
46
  muteAudio
47
+ mirror={mirror}
46
48
  VideoPlaceholder={VideoPlaceholder}
47
49
  ParticipantViewUI={ParticipantViewUI}
48
50
  />
@@ -63,6 +65,12 @@ export type PaginatedGridLayoutProps = {
63
65
  */
64
66
  excludeLocalParticipant?: boolean;
65
67
 
68
+ /**
69
+ * When set to `false` disables mirroring of the local partipant's video.
70
+ * @default true
71
+ */
72
+ mirrorLocalParticipantVideo?: boolean;
73
+
66
74
  /**
67
75
  * Turns on/off the pagination arrows.
68
76
  * @default true
@@ -76,6 +84,7 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
76
84
  ? props.groupSize || GROUP_SIZE
77
85
  : GROUP_SIZE,
78
86
  excludeLocalParticipant = false,
87
+ mirrorLocalParticipantVideo = true,
79
88
  pageArrowsVisible = true,
80
89
  VideoPlaceholder,
81
90
  ParticipantViewUI = DefaultParticipantViewUI,
@@ -121,6 +130,7 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
121
130
  }, [page, pageCount]);
122
131
 
123
132
  const selectedGroup = participantGroups[page];
133
+ const mirror = mirrorLocalParticipantVideo ? undefined : false;
124
134
 
125
135
  if (!call) return null;
126
136
 
@@ -143,6 +153,7 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
143
153
  {selectedGroup && (
144
154
  <PaginatedGridLayoutGroup
145
155
  group={selectedGroup}
156
+ mirror={mirror}
146
157
  VideoPlaceholder={VideoPlaceholder}
147
158
  ParticipantViewUI={ParticipantViewUI}
148
159
  />
@@ -41,6 +41,11 @@ export type SpeakerLayoutProps = {
41
41
  * @default false
42
42
  */
43
43
  excludeLocalParticipant?: boolean;
44
+ /**
45
+ * When set to `false` disables mirroring of the local partipant's video.
46
+ * @default true
47
+ */
48
+ mirrorLocalParticipantVideo?: boolean;
44
49
  /**
45
50
  * Turns on/off the pagination arrows.
46
51
  * @default true
@@ -58,6 +63,7 @@ export const SpeakerLayout = ({
58
63
  VideoPlaceholder,
59
64
  participantsBarPosition = 'bottom',
60
65
  participantsBarLimit,
66
+ mirrorLocalParticipantVideo = true,
61
67
  excludeLocalParticipant = false,
62
68
  pageArrowsVisible = true,
63
69
  }: SpeakerLayoutProps) => {
@@ -117,6 +123,8 @@ export const SpeakerLayout = ({
117
123
  );
118
124
  }
119
125
 
126
+ const mirror = mirrorLocalParticipantVideo ? undefined : false;
127
+
120
128
  if (!call) return null;
121
129
 
122
130
  return (
@@ -134,6 +142,7 @@ export const SpeakerLayout = ({
134
142
  <ParticipantView
135
143
  participant={participantInSpotlight}
136
144
  muteAudio={true}
145
+ mirror={mirror}
137
146
  trackType={
138
147
  isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack'
139
148
  }
@@ -164,6 +173,7 @@ export const SpeakerLayout = ({
164
173
  participant={participantInSpotlight}
165
174
  ParticipantViewUI={ParticipantViewUIBar}
166
175
  VideoPlaceholder={VideoPlaceholder}
176
+ mirror={mirror}
167
177
  muteAudio={true}
168
178
  />
169
179
  </div>
@@ -177,6 +187,7 @@ export const SpeakerLayout = ({
177
187
  participant={participant}
178
188
  ParticipantViewUI={ParticipantViewUIBar}
179
189
  VideoPlaceholder={VideoPlaceholder}
190
+ mirror={mirror}
180
191
  muteAudio={true}
181
192
  />
182
193
  </div>
@@ -11,9 +11,9 @@ import { useParticipantViewContext } from './ParticipantViewContext';
11
11
  import {
12
12
  GenericMenu,
13
13
  GenericMenuButtonItem,
14
- Icon,
15
14
  useMenuContext,
16
- } from '../../../components';
15
+ } from '../../../components/Menu';
16
+ import { Icon } from '../../../components/Icon';
17
17
 
18
18
  export const ParticipantActionsContextMenu = () => {
19
19
  const { participant, participantViewElement, videoElement } =
@@ -13,6 +13,7 @@ import {
13
13
  StreamVideoParticipant,
14
14
  VideoTrackType,
15
15
  } from '@stream-io/video-client';
16
+ import { useCallStateHooks } from '@stream-io/video-react-bindings';
16
17
 
17
18
  import { Audio } from '../Audio';
18
19
  import { Video, VideoProps } from '../Video';
@@ -41,6 +42,12 @@ export type ParticipantViewProps = {
41
42
  */
42
43
  trackType?: VideoTrackType | 'none';
43
44
 
45
+ /**
46
+ * Forces participant's video to be mirrored or unmirrored. By default, video track
47
+ * from the local participant is mirrored, and all other videos are not mirrored.
48
+ */
49
+ mirror?: boolean;
50
+
44
51
  /**
45
52
  * This prop is only useful for advanced use-cases (for example, building your own layout).
46
53
  * When set to `true` it will mute the give participant's audio stream on the client side.
@@ -68,6 +75,7 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
68
75
  {
69
76
  participant,
70
77
  trackType = 'videoTrack',
78
+ mirror,
71
79
  muteAudio,
72
80
  refs: { setVideoElement, setVideoPlaceholderElement } = {},
73
81
  className,
@@ -100,6 +108,9 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
100
108
  trackType,
101
109
  });
102
110
 
111
+ const { useIncomingVideoSettings } = useCallStateHooks();
112
+ const { isParticipantVideoEnabled } = useIncomingVideoSettings();
113
+
103
114
  const participantViewContextValue = useMemo(
104
115
  () => ({
105
116
  participant,
@@ -167,6 +178,12 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
167
178
  participant={participant}
168
179
  trackType={trackType}
169
180
  refs={videoRefs}
181
+ enabled={
182
+ isLocalParticipant ||
183
+ trackType !== 'videoTrack' ||
184
+ isParticipantVideoEnabled(participant.sessionId)
185
+ }
186
+ mirror={mirror}
170
187
  autoPlay
171
188
  />
172
189
  {isComponentType(ParticipantViewUI) ? (
@@ -20,6 +20,16 @@ import {
20
20
  import { useCall } from '@stream-io/video-react-bindings';
21
21
 
22
22
  export type VideoProps = ComponentPropsWithoutRef<'video'> & {
23
+ /**
24
+ * Pass false to disable rendering video and render fallback
25
+ * even if the participant has published video.
26
+ */
27
+ enabled?: boolean;
28
+ /**
29
+ * Forces the video to be mirrored or unmirrored. By default, video track
30
+ * from the local participant is mirrored, and all other videos are not mirrored.
31
+ */
32
+ mirror?: boolean;
23
33
  /**
24
34
  * The track type to display.
25
35
  */
@@ -54,6 +64,8 @@ export type VideoProps = ComponentPropsWithoutRef<'video'> & {
54
64
  };
55
65
 
56
66
  export const Video = ({
67
+ enabled,
68
+ mirror,
57
69
  trackType,
58
70
  participant,
59
71
  className,
@@ -139,8 +151,11 @@ export const Video = ({
139
151
  trackType === 'none' ||
140
152
  viewportVisibilityState?.[trackType] === VisibilityState.INVISIBLE;
141
153
 
142
- const hasNoVideoOrInvisible = !isPublishingTrack || isInvisible;
143
- const mirrorVideo = isLocalParticipant && trackType === 'videoTrack';
154
+ const hasNoVideoOrInvisible = !enabled || !isPublishingTrack || isInvisible;
155
+ const mirrorVideo =
156
+ mirror === undefined
157
+ ? isLocalParticipant && trackType === 'videoTrack'
158
+ : mirror;
144
159
  const isScreenShareTrack = trackType === 'screenShareTrack';
145
160
  return (
146
161
  <>