@stream-io/video-react-sdk 1.18.13 → 1.19.1

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.
@@ -83,6 +83,7 @@ export declare const translations: {
83
83
  "{{ direction }} picture-in-picture": string;
84
84
  "Dominant Speaker": string;
85
85
  "Poor connection quality": string;
86
+ "Video paused due to insufficient bandwidth": string;
86
87
  Participants: string;
87
88
  Anonymous: string;
88
89
  "No participants found": string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "1.18.13",
3
+ "version": "1.19.1",
4
4
  "main": "./dist/index.cjs.js",
5
5
  "module": "./dist/index.es.js",
6
6
  "types": "./dist/index.d.ts",
@@ -30,9 +30,9 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@floating-ui/react": "^0.27.6",
33
- "@stream-io/video-client": "1.26.1",
33
+ "@stream-io/video-client": "1.27.1",
34
34
  "@stream-io/video-filters-web": "0.2.1",
35
- "@stream-io/video-react-bindings": "1.7.8",
35
+ "@stream-io/video-react-bindings": "1.7.10",
36
36
  "chart.js": "^4.4.4",
37
37
  "clsx": "^2.0.0",
38
38
  "react-chartjs-2": "^5.3.0"
@@ -45,8 +45,8 @@
45
45
  "@rollup/plugin-json": "^6.1.0",
46
46
  "@rollup/plugin-replace": "^6.0.2",
47
47
  "@rollup/plugin-typescript": "^12.1.2",
48
- "@stream-io/audio-filters-web": "^0.4.2",
49
- "@stream-io/video-styling": "^1.3.0",
48
+ "@stream-io/audio-filters-web": "^0.4.3",
49
+ "@stream-io/video-styling": "^1.4.0",
50
50
  "@types/react": "^19.1.3",
51
51
  "@types/react-dom": "^19.1.3",
52
52
  "react": "19.0.0",
@@ -10,7 +10,7 @@ import {
10
10
  import { flushSync } from 'react-dom';
11
11
  import clsx from 'clsx';
12
12
  import { useCall } from '@stream-io/video-react-bindings';
13
- import { disposeOfMediaStream, getLogger } from '@stream-io/video-client';
13
+ import { Call, disposeOfMediaStream, getLogger } from '@stream-io/video-client';
14
14
  import {
15
15
  BackgroundBlurLevel,
16
16
  BackgroundFilter,
@@ -228,7 +228,7 @@ export const BackgroundFiltersProvider = (
228
228
 
229
229
  const BackgroundFilters = (props: { tfLite: TFLite }) => {
230
230
  const call = useCall();
231
- const { children, start } = useRenderer(props.tfLite);
231
+ const { children, start } = useRenderer(props.tfLite, call);
232
232
  const { backgroundFilter, onError } = useBackgroundFilters();
233
233
  const handleErrorRef = useRef<((error: any) => void) | undefined>(undefined);
234
234
  handleErrorRef.current = onError;
@@ -246,7 +246,7 @@ const BackgroundFilters = (props: { tfLite: TFLite }) => {
246
246
  return children;
247
247
  };
248
248
 
249
- const useRenderer = (tfLite: TFLite) => {
249
+ const useRenderer = (tfLite: TFLite, call: Call | undefined) => {
250
250
  const { backgroundFilter, backgroundBlurLevel, backgroundImage } =
251
251
  useBackgroundFilters();
252
252
  const videoRef = useRef<HTMLVideoElement>(null);
@@ -297,6 +297,11 @@ const useRenderer = (tfLite: TFLite) => {
297
297
  height: trackSettings.height ?? 0,
298
298
  }),
299
299
  );
300
+ call?.tracer.trace('backgroundFilters.enable', {
301
+ backgroundFilter,
302
+ backgroundBlurLevel,
303
+ backgroundImage,
304
+ });
300
305
  renderer = createRenderer(
301
306
  tfLite,
302
307
  videoEl,
@@ -320,13 +325,20 @@ const useRenderer = (tfLite: TFLite) => {
320
325
  return {
321
326
  output,
322
327
  stop: () => {
328
+ call?.tracer.trace('backgroundFilters.disable', null);
323
329
  renderer?.dispose();
324
330
  if (videoRef.current) videoRef.current.srcObject = null;
325
331
  if (outputStream) disposeOfMediaStream(outputStream);
326
332
  },
327
333
  };
328
334
  },
329
- [backgroundBlurLevel, backgroundFilter, backgroundImage, tfLite],
335
+ [
336
+ backgroundBlurLevel,
337
+ backgroundFilter,
338
+ backgroundImage,
339
+ call?.tracer,
340
+ tfLite,
341
+ ],
330
342
  );
331
343
 
332
344
  const children = (
@@ -2,6 +2,7 @@ import { ComponentType, forwardRef } from 'react';
2
2
  import { Placement } from '@floating-ui/react';
3
3
  import {
4
4
  hasAudio,
5
+ hasPausedTrack,
5
6
  hasScreenShare,
6
7
  hasVideo,
7
8
  SfuModels,
@@ -50,7 +51,9 @@ export const DefaultScreenShareOverlay = () => {
50
51
  const { t } = useI18n();
51
52
 
52
53
  const stopScreenShare = () => {
53
- call?.screenShare.disable();
54
+ call?.screenShare.disable().catch((err) => {
55
+ console.error('Failed to stop screen sharing:', err);
56
+ });
54
57
  };
55
58
 
56
59
  return (
@@ -61,6 +64,7 @@ export const DefaultScreenShareOverlay = () => {
61
64
  </span>
62
65
  <button
63
66
  onClick={stopScreenShare}
67
+ type="button"
64
68
  className="str-video__screen-share-overlay__button"
65
69
  >
66
70
  <Icon icon="close" /> {t('Stop Screen Sharing')}
@@ -111,7 +115,7 @@ export const DefaultParticipantViewUI = ({
111
115
  export const ParticipantDetails = ({
112
116
  indicatorsVisible = true,
113
117
  }: Pick<DefaultParticipantViewUIProps, 'indicatorsVisible'>) => {
114
- const { participant } = useParticipantViewContext();
118
+ const { participant, trackType } = useParticipantViewContext();
115
119
  const {
116
120
  isLocalParticipant,
117
121
  connectionQuality,
@@ -130,6 +134,8 @@ export const ParticipantDetails = ({
130
134
  const hasAudioTrack = hasAudio(participant);
131
135
  const hasVideoTrack = hasVideo(participant);
132
136
  const canUnpin = !!pin && pin.isLocalPin;
137
+ const isTrackPaused =
138
+ trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;
133
139
 
134
140
  return (
135
141
  <>
@@ -143,6 +149,12 @@ export const ParticipantDetails = ({
143
149
  {indicatorsVisible && !hasVideoTrack && (
144
150
  <span className="str-video__participant-details__name--video-muted" />
145
151
  )}
152
+ {indicatorsVisible && isTrackPaused && (
153
+ <span
154
+ title={t('Video paused due to insufficient bandwidth')}
155
+ className="str-video__participant-details__name--track-paused"
156
+ />
157
+ )}
146
158
  {indicatorsVisible && canUnpin && (
147
159
  // TODO: remove this monstrosity once we have a proper design
148
160
  <span
@@ -6,6 +6,7 @@ import {
6
6
  useState,
7
7
  } from 'react';
8
8
  import {
9
+ hasPausedTrack,
9
10
  hasScreenShare,
10
11
  hasVideo,
11
12
  StreamVideoParticipant,
@@ -171,7 +172,11 @@ export const Video = ({
171
172
  trackType === 'none' ||
172
173
  viewportVisibilityState?.[trackType] === VisibilityState.INVISIBLE;
173
174
 
174
- const hasNoVideoOrInvisible = !enabled || !isPublishingTrack || isInvisible;
175
+ const hasNoVideoOrInvisible =
176
+ !enabled ||
177
+ !isPublishingTrack ||
178
+ isInvisible ||
179
+ hasPausedTrack(participant, trackType);
175
180
  const mirrorVideo =
176
181
  mirror === undefined
177
182
  ? isLocalParticipant && trackType === 'videoTrack'
@@ -88,6 +88,7 @@
88
88
 
89
89
  "Dominant Speaker": "Dominant Speaker",
90
90
  "Poor connection quality": "Poor connection quality. Please check your internet connection.",
91
+ "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
91
92
 
92
93
  "Participants": "Participants",
93
94
  "Anonymous": ", and ({{ count }}) anonymous",