@stream-io/video-react-sdk 1.19.8 → 1.20.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.
@@ -8,7 +8,7 @@ export type UseSearchParams<T> = {
8
8
  /** Search function performing the search request */
9
9
  searchFn: (searchQuery: string) => Promise<T[]>;
10
10
  /** Debounce interval applied to the search function */
11
- debounceInterval: number;
11
+ debounceInterval?: number;
12
12
  /** Search query string */
13
13
  searchQuery?: string;
14
14
  };
@@ -65,6 +65,7 @@ export declare const translations: {
65
65
  "Pin for everyone": string;
66
66
  "Unpin for everyone": string;
67
67
  Block: string;
68
+ Kick: string;
68
69
  "Turn off video": string;
69
70
  "Turn off screen share": string;
70
71
  "Mute audio": string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "1.19.8",
3
+ "version": "1.20.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.27.5",
33
+ "@stream-io/video-client": "1.28.1",
34
34
  "@stream-io/video-filters-web": "0.2.1",
35
- "@stream-io/video-react-bindings": "1.7.14",
35
+ "@stream-io/video-react-bindings": "1.7.16",
36
36
  "chart.js": "^4.4.4",
37
37
  "clsx": "^2.0.0",
38
38
  "react-chartjs-2": "^5.3.0"
@@ -46,7 +46,7 @@
46
46
  "@rollup/plugin-replace": "^6.0.2",
47
47
  "@rollup/plugin-typescript": "^12.1.2",
48
48
  "@stream-io/audio-filters-web": "^0.4.3",
49
- "@stream-io/video-styling": "^1.4.0",
49
+ "@stream-io/video-styling": "^1.5.0",
50
50
  "@types/react": "^19.1.3",
51
51
  "@types/react-dom": "^19.1.3",
52
52
  "react": "19.0.0",
@@ -47,8 +47,6 @@ const UserListTypes = {
47
47
  blocked: 'Blocked users',
48
48
  } as const;
49
49
 
50
- const DEFAULT_DEBOUNCE_SEARCH_INTERVAL = 200 as const;
51
-
52
50
  export const CallParticipantsList = ({
53
51
  onClose,
54
52
  activeUsersSearchFn,
@@ -103,9 +101,9 @@ const CallParticipantListContentHeader = ({
103
101
  }) => {
104
102
  const call = useCall();
105
103
 
106
- const muteAll = () => {
104
+ const muteAll = useCallback(() => {
107
105
  call?.muteAllUsers('audio');
108
- };
106
+ }, [call]);
109
107
 
110
108
  return (
111
109
  <div className="str-video__participant-list__content-header">
@@ -141,7 +139,7 @@ const CallParticipantListContentHeader = ({
141
139
  const ActiveUsersSearchResults = ({
142
140
  searchQuery,
143
141
  activeUsersSearchFn: activeUsersSearchFnFromProps,
144
- debounceSearchInterval = DEFAULT_DEBOUNCE_SEARCH_INTERVAL,
142
+ debounceSearchInterval,
145
143
  }: Pick<
146
144
  CallParticipantListProps,
147
145
  'activeUsersSearchFn' | 'debounceSearchInterval'
@@ -150,23 +148,18 @@ const ActiveUsersSearchResults = ({
150
148
  const participants = useParticipants({ sortBy: name });
151
149
 
152
150
  const activeUsersSearchFn = useCallback(
153
- (queryString: string) => {
151
+ async (queryString: string) => {
154
152
  const queryRegExp = new RegExp(queryString, 'i');
155
- return Promise.resolve(
156
- participants.filter((participant) => {
157
- return participant.name.match(queryRegExp);
158
- }),
159
- );
153
+ return participants.filter((p) => p.name.match(queryRegExp));
160
154
  },
161
155
  [participants],
162
156
  );
163
157
 
164
- const { searchQueryInProgress, searchResults } =
165
- useSearch<StreamVideoParticipant>({
166
- searchFn: activeUsersSearchFnFromProps ?? activeUsersSearchFn,
167
- debounceInterval: debounceSearchInterval,
168
- searchQuery,
169
- });
158
+ const { searchQueryInProgress, searchResults } = useSearch({
159
+ searchFn: activeUsersSearchFnFromProps ?? activeUsersSearchFn,
160
+ debounceInterval: debounceSearchInterval,
161
+ searchQuery,
162
+ });
170
163
 
171
164
  return (
172
165
  <SearchResults<StreamVideoParticipant>
@@ -181,7 +174,7 @@ const ActiveUsersSearchResults = ({
181
174
 
182
175
  const BlockedUsersSearchResults = ({
183
176
  blockedUsersSearchFn: blockedUsersSearchFnFromProps,
184
- debounceSearchInterval = DEFAULT_DEBOUNCE_SEARCH_INTERVAL,
177
+ debounceSearchInterval,
185
178
  searchQuery,
186
179
  }: Pick<
187
180
  CallParticipantListProps,
@@ -191,18 +184,14 @@ const BlockedUsersSearchResults = ({
191
184
  const blockedUsers = useCallBlockedUserIds();
192
185
 
193
186
  const blockedUsersSearchFn = useCallback(
194
- (queryString: string) => {
187
+ async (queryString: string) => {
195
188
  const queryRegExp = new RegExp(queryString, 'i');
196
- return Promise.resolve(
197
- blockedUsers.filter((blockedUser) => {
198
- return blockedUser.match(queryRegExp);
199
- }),
200
- );
189
+ return blockedUsers.filter((userId) => userId.match(queryRegExp));
201
190
  },
202
191
  [blockedUsers],
203
192
  );
204
193
 
205
- const { searchQueryInProgress, searchResults } = useSearch<string>({
194
+ const { searchQueryInProgress, searchResults } = useSearch({
206
195
  searchFn: blockedUsersSearchFnFromProps ?? blockedUsersSearchFn,
207
196
  debounceInterval: debounceSearchInterval,
208
197
  searchQuery,
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  export type SearchController<T> = {
4
4
  /** Flag signals that the search request is flight await the response */
@@ -11,19 +11,22 @@ export type UseSearchParams<T> = {
11
11
  /** Search function performing the search request */
12
12
  searchFn: (searchQuery: string) => Promise<T[]>;
13
13
  /** Debounce interval applied to the search function */
14
- debounceInterval: number;
14
+ debounceInterval?: number;
15
15
  /** Search query string */
16
16
  searchQuery?: string;
17
17
  };
18
18
 
19
19
  export const useSearch = <T>({
20
- debounceInterval,
20
+ debounceInterval = 200,
21
21
  searchFn,
22
22
  searchQuery = '',
23
23
  }: UseSearchParams<T>): SearchController<T> => {
24
24
  const [searchResults, setSearchResults] = useState<T[]>([]);
25
25
  const [searchQueryInProgress, setSearchQueryInProgress] = useState(false);
26
26
 
27
+ const searchFnRef = useRef(searchFn);
28
+ searchFnRef.current = searchFn;
29
+
27
30
  useEffect(() => {
28
31
  if (!searchQuery.length) {
29
32
  setSearchQueryInProgress(false);
@@ -35,7 +38,7 @@ export const useSearch = <T>({
35
38
 
36
39
  const timeout = setTimeout(async () => {
37
40
  try {
38
- const results = await searchFn(searchQuery);
41
+ const results = await searchFnRef.current(searchQuery);
39
42
  setSearchResults(results);
40
43
  } catch (error) {
41
44
  console.error(error);
@@ -47,7 +50,7 @@ export const useSearch = <T>({
47
50
  return () => {
48
51
  clearTimeout(timeout);
49
52
  };
50
- }, [debounceInterval, searchFn, searchQuery]);
53
+ }, [debounceInterval, searchQuery]);
51
54
 
52
55
  return {
53
56
  searchQueryInProgress,
@@ -34,6 +34,7 @@ export const ParticipantActionsContextMenu = () => {
34
34
  const hasScreenShareAudioTrack = hasScreenShareAudio(participant);
35
35
 
36
36
  const blockUser = () => call?.blockUser(userId);
37
+ const kickUser = () => call?.kickUser({ user_id: userId });
37
38
  const muteAudio = () => call?.muteUser(userId, 'audio');
38
39
  const muteVideo = () => call?.muteUser(userId, 'video');
39
40
  const muteScreenShare = () => call?.muteUser(userId, 'screenshare');
@@ -64,10 +65,7 @@ export const ParticipantActionsContextMenu = () => {
64
65
 
65
66
  const pinForEveryone = () => {
66
67
  call
67
- ?.pinForEveryone({
68
- user_id: userId,
69
- session_id: sessionId,
70
- })
68
+ ?.pinForEveryone({ user_id: userId, session_id: sessionId })
71
69
  .catch((err) => {
72
70
  console.error(`Failed to pin participant ${userId}`, err);
73
71
  });
@@ -75,10 +73,7 @@ export const ParticipantActionsContextMenu = () => {
75
73
 
76
74
  const unpinForEveryone = () => {
77
75
  call
78
- ?.unpinForEveryone({
79
- user_id: userId,
80
- session_id: sessionId,
81
- })
76
+ ?.unpinForEveryone({ user_id: userId, session_id: sessionId })
82
77
  .catch((err) => {
83
78
  console.error(`Failed to unpin participant ${userId}`, err);
84
79
  });
@@ -145,6 +140,12 @@ export const ParticipantActionsContextMenu = () => {
145
140
  {t('Block')}
146
141
  </GenericMenuButtonItem>
147
142
  </Restricted>
143
+ <Restricted requiredGrants={[OwnCapability.KICK_USER]}>
144
+ <GenericMenuButtonItem onClick={kickUser}>
145
+ <Icon icon="kick-user" />
146
+ {t('Kick')}
147
+ </GenericMenuButtonItem>
148
+ </Restricted>
148
149
  <Restricted requiredGrants={[OwnCapability.MUTE_USERS]}>
149
150
  {hasVideoTrack && (
150
151
  <GenericMenuButtonItem onClick={muteVideo}>
@@ -69,6 +69,7 @@
69
69
  "Pin for everyone": "Pin for everyone",
70
70
  "Unpin for everyone": "Unpin for everyone",
71
71
  "Block": "Block",
72
+ "Kick": "Kick",
72
73
  "Turn off video": "Turn off video",
73
74
  "Turn off screen share": "Turn off screen share",
74
75
  "Mute audio": "Mute audio",