@stream-io/video-react-sdk 1.14.0 → 1.14.2
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 +50 -13
- package/dist/css/styles.css +12 -0
- package/dist/css/styles.css.map +1 -1
- package/dist/index.cjs.js +44 -9
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +43 -10
- package/dist/index.es.js.map +1 -1
- package/dist/src/core/components/CallLayout/PaginatedGridLayout.d.ts +5 -1
- package/dist/src/core/components/CallLayout/PipLayout.d.ts +43 -0
- package/dist/src/core/components/CallLayout/SpeakerLayout.d.ts +5 -1
- package/dist/src/core/components/CallLayout/hooks.d.ts +2 -0
- package/dist/src/core/components/CallLayout/index.d.ts +2 -0
- package/dist/src/hooks/useFloatingUIPreset.d.ts +4 -4
- package/package.json +5 -5
- package/src/components/Menu/MenuToggle.tsx +8 -4
- package/src/core/components/CallLayout/PaginatedGridLayout.tsx +13 -2
- package/src/core/components/CallLayout/PipLayout.tsx +110 -0
- package/src/core/components/CallLayout/SpeakerLayout.tsx +14 -1
- package/src/core/components/CallLayout/hooks.ts +9 -1
- package/src/core/components/CallLayout/index.ts +2 -0
|
@@ -39,7 +39,11 @@ export type PaginatedGridLayoutProps = {
|
|
|
39
39
|
* @default true
|
|
40
40
|
*/
|
|
41
41
|
pageArrowsVisible?: boolean;
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Whether the layout is muted. Defaults to `false`.
|
|
44
|
+
*/
|
|
45
|
+
muted?: boolean;
|
|
46
|
+
} & Pick<ParticipantViewProps, 'ParticipantViewUI' | 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
43
47
|
export declare const PaginatedGridLayout: {
|
|
44
48
|
(props: PaginatedGridLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
45
49
|
displayName: string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ParticipantViewProps } from '../ParticipantView';
|
|
2
|
+
import { ParticipantFilter, ParticipantPredicate } from './hooks';
|
|
3
|
+
export type PipLayoutProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Whether to exclude the local participant from the grid.
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
|
+
excludeLocalParticipant?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Predicate to filter call participants or a filter object.
|
|
11
|
+
* @example
|
|
12
|
+
* // With a predicate:
|
|
13
|
+
* <PipLayout
|
|
14
|
+
* filterParticipants={p => p.roles.includes('student')}
|
|
15
|
+
* />
|
|
16
|
+
* @example
|
|
17
|
+
* // With a filter object:
|
|
18
|
+
* <PipLayout
|
|
19
|
+
* filterParticipants={{
|
|
20
|
+
* $or: [
|
|
21
|
+
* { roles: { $contains: 'student' } },
|
|
22
|
+
* { isPinned: true },
|
|
23
|
+
* ],
|
|
24
|
+
* }}
|
|
25
|
+
* />
|
|
26
|
+
*/
|
|
27
|
+
filterParticipants?: ParticipantPredicate | ParticipantFilter;
|
|
28
|
+
/**
|
|
29
|
+
* When set to `false` disables mirroring of the local partipant's video.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
mirrorLocalParticipantVideo?: boolean;
|
|
33
|
+
} & Pick<ParticipantViewProps, 'ParticipantViewUI' | 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
34
|
+
export declare const PipLayout: {
|
|
35
|
+
Pip: {
|
|
36
|
+
(props: PipLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
37
|
+
displayName: string;
|
|
38
|
+
};
|
|
39
|
+
Host: {
|
|
40
|
+
(): import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
displayName: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -53,8 +53,12 @@ export type SpeakerLayoutProps = {
|
|
|
53
53
|
* @default true
|
|
54
54
|
*/
|
|
55
55
|
pageArrowsVisible?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the layout is muted. Defaults to `false`.
|
|
58
|
+
*/
|
|
59
|
+
muted?: boolean;
|
|
56
60
|
} & Pick<ParticipantViewProps, 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
57
61
|
export declare const SpeakerLayout: {
|
|
58
|
-
({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition, participantsBarLimit, mirrorLocalParticipantVideo, excludeLocalParticipant, filterParticipants, pageArrowsVisible, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
62
|
+
({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, PictureInPicturePlaceholder, participantsBarPosition, participantsBarLimit, mirrorLocalParticipantVideo, excludeLocalParticipant, filterParticipants, pageArrowsVisible, muted, }: SpeakerLayoutProps): import("react/jsx-runtime").JSX.Element | null;
|
|
59
63
|
displayName: string;
|
|
60
64
|
};
|
|
@@ -2,6 +2,8 @@ import { Call, StreamVideoParticipant } from '@stream-io/video-client';
|
|
|
2
2
|
import { Filter } from '../../../utilities/filter';
|
|
3
3
|
export type FilterableParticipant = Pick<StreamVideoParticipant, 'userId' | 'isSpeaking' | 'isDominantSpeaker' | 'name' | 'roles'> & {
|
|
4
4
|
isPinned: boolean;
|
|
5
|
+
hasVideo: boolean;
|
|
6
|
+
hasAudio: boolean;
|
|
5
7
|
};
|
|
6
8
|
export type ParticipantFilter = Filter<FilterableParticipant>;
|
|
7
9
|
export type ParticipantPredicate = (paritcipant: StreamVideoParticipant) => boolean;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from './LivestreamLayout';
|
|
2
2
|
export * from './PaginatedGridLayout';
|
|
3
3
|
export * from './SpeakerLayout';
|
|
4
|
+
export * from './PipLayout';
|
|
5
|
+
export { useFilteredParticipants } from './hooks';
|
|
4
6
|
export type { FilterableParticipant, ParticipantFilter, ParticipantPredicate, } from './hooks';
|
|
@@ -12,12 +12,12 @@ export declare const useFloatingUIPreset: ({ middleware, placement, strategy, of
|
|
|
12
12
|
y: number;
|
|
13
13
|
domReference: Element | null;
|
|
14
14
|
floating: HTMLElement | null;
|
|
15
|
-
strategy: import("@floating-ui/
|
|
15
|
+
strategy: import("@floating-ui/react").Strategy | undefined;
|
|
16
16
|
context: {
|
|
17
17
|
x: number;
|
|
18
18
|
y: number;
|
|
19
|
-
placement: import("@floating-ui/
|
|
20
|
-
strategy: import("@floating-ui/
|
|
19
|
+
placement: import("@floating-ui/react").Placement;
|
|
20
|
+
strategy: import("@floating-ui/react").Strategy;
|
|
21
21
|
middlewareData: import("@floating-ui/core").MiddlewareData;
|
|
22
22
|
isPositioned: boolean;
|
|
23
23
|
update: () => void;
|
|
@@ -27,7 +27,7 @@ export declare const useFloatingUIPreset: ({ middleware, placement, strategy, of
|
|
|
27
27
|
events: import("@floating-ui/react").FloatingEvents;
|
|
28
28
|
dataRef: React.MutableRefObject<import("@floating-ui/react").ContextData>;
|
|
29
29
|
nodeId: string | undefined;
|
|
30
|
-
floatingId: string;
|
|
30
|
+
floatingId: string | undefined;
|
|
31
31
|
refs: import("@floating-ui/react").ExtendedRefs<import("@floating-ui/react").ReferenceType>;
|
|
32
32
|
elements: import("@floating-ui/react").ExtendedElements<import("@floating-ui/react").ReferenceType>;
|
|
33
33
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-sdk",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.2",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"module": "./dist/index.es.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"*.css"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@floating-ui/react": "^0.
|
|
33
|
-
"@stream-io/video-client": "1.19.
|
|
32
|
+
"@floating-ui/react": "^0.27.6",
|
|
33
|
+
"@stream-io/video-client": "1.19.2",
|
|
34
34
|
"@stream-io/video-filters-web": "0.1.7",
|
|
35
|
-
"@stream-io/video-react-bindings": "1.5.
|
|
35
|
+
"@stream-io/video-react-bindings": "1.5.14",
|
|
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.2.3",
|
|
49
|
-
"@stream-io/video-styling": "^1.1.
|
|
49
|
+
"@stream-io/video-styling": "^1.1.3",
|
|
50
50
|
"@types/react": "^18.3.2",
|
|
51
51
|
"@types/react-dom": "^18.3.0",
|
|
52
52
|
"react": "^18.3.1",
|
|
@@ -98,6 +98,8 @@ export const MenuToggle = <E extends HTMLElement>({
|
|
|
98
98
|
});
|
|
99
99
|
|
|
100
100
|
useEffect(() => {
|
|
101
|
+
const parentDocument = domReference?.ownerDocument;
|
|
102
|
+
|
|
101
103
|
const handleClick = (event: MouseEvent) => {
|
|
102
104
|
if (!floating && domReference?.contains(event.target as Node)) {
|
|
103
105
|
setMenuShown(true);
|
|
@@ -119,11 +121,13 @@ export const MenuToggle = <E extends HTMLElement>({
|
|
|
119
121
|
toggleHandler.current?.(false);
|
|
120
122
|
}
|
|
121
123
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
parentDocument?.addEventListener('click', handleClick, { capture: true });
|
|
125
|
+
parentDocument?.addEventListener('keydown', handleKeyDown);
|
|
124
126
|
return () => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
parentDocument?.removeEventListener('click', handleClick, {
|
|
128
|
+
capture: true,
|
|
129
|
+
});
|
|
130
|
+
parentDocument?.removeEventListener('keydown', handleKeyDown);
|
|
127
131
|
};
|
|
128
132
|
}, [floating, domReference]);
|
|
129
133
|
|
|
@@ -106,7 +106,15 @@ export type PaginatedGridLayoutProps = {
|
|
|
106
106
|
* @default true
|
|
107
107
|
*/
|
|
108
108
|
pageArrowsVisible?: boolean;
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Whether the layout is muted. Defaults to `false`.
|
|
112
|
+
*/
|
|
113
|
+
muted?: boolean;
|
|
114
|
+
} & Pick<
|
|
115
|
+
ParticipantViewProps,
|
|
116
|
+
'ParticipantViewUI' | 'VideoPlaceholder' | 'PictureInPicturePlaceholder'
|
|
117
|
+
>;
|
|
110
118
|
|
|
111
119
|
export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
|
|
112
120
|
const {
|
|
@@ -119,6 +127,8 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
|
|
|
119
127
|
pageArrowsVisible = true,
|
|
120
128
|
VideoPlaceholder,
|
|
121
129
|
ParticipantViewUI = DefaultParticipantViewUI,
|
|
130
|
+
PictureInPicturePlaceholder,
|
|
131
|
+
muted,
|
|
122
132
|
} = props;
|
|
123
133
|
const [page, setPage] = useState(0);
|
|
124
134
|
const [
|
|
@@ -169,7 +179,7 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
|
|
|
169
179
|
className="str-video__paginated-grid-layout__wrapper"
|
|
170
180
|
ref={setPaginatedGridLayoutWrapperElement}
|
|
171
181
|
>
|
|
172
|
-
<ParticipantsAudio participants={remoteParticipants} />
|
|
182
|
+
{!muted && <ParticipantsAudio participants={remoteParticipants} />}
|
|
173
183
|
<div className="str-video__paginated-grid-layout">
|
|
174
184
|
{pageArrowsVisible && pageCount > 1 && (
|
|
175
185
|
<IconButton
|
|
@@ -186,6 +196,7 @@ export const PaginatedGridLayout = (props: PaginatedGridLayoutProps) => {
|
|
|
186
196
|
mirror={mirror}
|
|
187
197
|
VideoPlaceholder={VideoPlaceholder}
|
|
188
198
|
ParticipantViewUI={ParticipantViewUI}
|
|
199
|
+
PictureInPicturePlaceholder={PictureInPicturePlaceholder}
|
|
189
200
|
/>
|
|
190
201
|
)}
|
|
191
202
|
{pageArrowsVisible && pageCount > 1 && (
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ParticipantsAudio } from '../Audio';
|
|
5
|
+
import {
|
|
6
|
+
DefaultParticipantViewUI,
|
|
7
|
+
ParticipantView,
|
|
8
|
+
ParticipantViewProps,
|
|
9
|
+
} from '../ParticipantView';
|
|
10
|
+
import {
|
|
11
|
+
ParticipantFilter,
|
|
12
|
+
ParticipantPredicate,
|
|
13
|
+
useFilteredParticipants,
|
|
14
|
+
usePaginatedLayoutSortPreset,
|
|
15
|
+
} from './hooks';
|
|
16
|
+
|
|
17
|
+
export type PipLayoutProps = {
|
|
18
|
+
/**
|
|
19
|
+
* Whether to exclude the local participant from the grid.
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
excludeLocalParticipant?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Predicate to filter call participants or a filter object.
|
|
26
|
+
* @example
|
|
27
|
+
* // With a predicate:
|
|
28
|
+
* <PipLayout
|
|
29
|
+
* filterParticipants={p => p.roles.includes('student')}
|
|
30
|
+
* />
|
|
31
|
+
* @example
|
|
32
|
+
* // With a filter object:
|
|
33
|
+
* <PipLayout
|
|
34
|
+
* filterParticipants={{
|
|
35
|
+
* $or: [
|
|
36
|
+
* { roles: { $contains: 'student' } },
|
|
37
|
+
* { isPinned: true },
|
|
38
|
+
* ],
|
|
39
|
+
* }}
|
|
40
|
+
* />
|
|
41
|
+
*/
|
|
42
|
+
filterParticipants?: ParticipantPredicate | ParticipantFilter;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* When set to `false` disables mirroring of the local partipant's video.
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
mirrorLocalParticipantVideo?: boolean;
|
|
49
|
+
} & Pick<
|
|
50
|
+
ParticipantViewProps,
|
|
51
|
+
'ParticipantViewUI' | 'VideoPlaceholder' | 'PictureInPicturePlaceholder'
|
|
52
|
+
>;
|
|
53
|
+
|
|
54
|
+
const Pip = (props: PipLayoutProps) => {
|
|
55
|
+
const {
|
|
56
|
+
excludeLocalParticipant = false,
|
|
57
|
+
filterParticipants,
|
|
58
|
+
mirrorLocalParticipantVideo = true,
|
|
59
|
+
VideoPlaceholder,
|
|
60
|
+
ParticipantViewUI = DefaultParticipantViewUI,
|
|
61
|
+
PictureInPicturePlaceholder,
|
|
62
|
+
} = props;
|
|
63
|
+
const [layoutWrapperElement, setLayoutWrapperElement] =
|
|
64
|
+
useState<HTMLDivElement | null>(null);
|
|
65
|
+
|
|
66
|
+
const call = useCall();
|
|
67
|
+
const participants = useFilteredParticipants({
|
|
68
|
+
excludeLocalParticipant,
|
|
69
|
+
filterParticipants,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
usePaginatedLayoutSortPreset(call);
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!layoutWrapperElement || !call) return;
|
|
76
|
+
return call.setViewport(layoutWrapperElement);
|
|
77
|
+
}, [layoutWrapperElement, call]);
|
|
78
|
+
|
|
79
|
+
const mirror = mirrorLocalParticipantVideo ? undefined : false;
|
|
80
|
+
|
|
81
|
+
if (!call) return null;
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className="str-video__pip-layout" ref={setLayoutWrapperElement}>
|
|
85
|
+
{participants.map((participant) => (
|
|
86
|
+
<ParticipantView
|
|
87
|
+
key={participant.sessionId}
|
|
88
|
+
participant={participant}
|
|
89
|
+
muteAudio
|
|
90
|
+
mirror={mirror}
|
|
91
|
+
VideoPlaceholder={VideoPlaceholder}
|
|
92
|
+
PictureInPicturePlaceholder={PictureInPicturePlaceholder}
|
|
93
|
+
ParticipantViewUI={ParticipantViewUI}
|
|
94
|
+
/>
|
|
95
|
+
))}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
Pip.displayName = 'PipLayout.Pip';
|
|
101
|
+
|
|
102
|
+
const Host = () => {
|
|
103
|
+
const { useRemoteParticipants } = useCallStateHooks();
|
|
104
|
+
const remoteParticipants = useRemoteParticipants();
|
|
105
|
+
return <ParticipantsAudio participants={remoteParticipants} />;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
Host.displayName = 'PipLayout.Host';
|
|
109
|
+
|
|
110
|
+
export const PipLayout = { Pip, Host };
|
|
@@ -27,25 +27,30 @@ export type SpeakerLayoutProps = {
|
|
|
27
27
|
* The UI to be used for the participant in the spotlight.
|
|
28
28
|
*/
|
|
29
29
|
ParticipantViewUISpotlight?: ParticipantViewProps['ParticipantViewUI'];
|
|
30
|
+
|
|
30
31
|
/**
|
|
31
32
|
* The UI to be used for the participants in the participants bar.
|
|
32
33
|
*/
|
|
33
34
|
ParticipantViewUIBar?: ParticipantViewProps['ParticipantViewUI'];
|
|
35
|
+
|
|
34
36
|
/**
|
|
35
37
|
* The position of the participants who are not in focus.
|
|
36
38
|
* Providing `null` will hide the bar.
|
|
37
39
|
*/
|
|
38
40
|
participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;
|
|
41
|
+
|
|
39
42
|
/**
|
|
40
43
|
* Hard limits the number of the participants rendered in the participants bar.
|
|
41
44
|
* Providing string `dynamic` will calculate hard limit based on screen width/height.
|
|
42
45
|
*/
|
|
43
46
|
participantsBarLimit?: 'dynamic' | number;
|
|
47
|
+
|
|
44
48
|
/**
|
|
45
49
|
* When set to `true` will exclude the local participant from layout.
|
|
46
50
|
* @default false
|
|
47
51
|
*/
|
|
48
52
|
excludeLocalParticipant?: boolean;
|
|
53
|
+
|
|
49
54
|
/**
|
|
50
55
|
* Predicate to filter call participants or a filter object.
|
|
51
56
|
* @example
|
|
@@ -65,16 +70,23 @@ export type SpeakerLayoutProps = {
|
|
|
65
70
|
* />
|
|
66
71
|
*/
|
|
67
72
|
filterParticipants?: ParticipantPredicate | ParticipantFilter;
|
|
73
|
+
|
|
68
74
|
/**
|
|
69
75
|
* When set to `false` disables mirroring of the local participant's video.
|
|
70
76
|
* @default true
|
|
71
77
|
*/
|
|
72
78
|
mirrorLocalParticipantVideo?: boolean;
|
|
79
|
+
|
|
73
80
|
/**
|
|
74
81
|
* Turns on/off the pagination arrows.
|
|
75
82
|
* @default true
|
|
76
83
|
*/
|
|
77
84
|
pageArrowsVisible?: boolean;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Whether the layout is muted. Defaults to `false`.
|
|
88
|
+
*/
|
|
89
|
+
muted?: boolean;
|
|
78
90
|
} & Pick<
|
|
79
91
|
ParticipantViewProps,
|
|
80
92
|
'VideoPlaceholder' | 'PictureInPicturePlaceholder'
|
|
@@ -95,6 +107,7 @@ export const SpeakerLayout = ({
|
|
|
95
107
|
excludeLocalParticipant = false,
|
|
96
108
|
filterParticipants,
|
|
97
109
|
pageArrowsVisible = true,
|
|
110
|
+
muted,
|
|
98
111
|
}: SpeakerLayoutProps) => {
|
|
99
112
|
const call = useCall();
|
|
100
113
|
const { useParticipants, useRemoteParticipants } = useCallStateHooks();
|
|
@@ -160,7 +173,7 @@ export const SpeakerLayout = ({
|
|
|
160
173
|
(participantsWithAppliedLimit.length > 0 || isSpeakerScreenSharing);
|
|
161
174
|
return (
|
|
162
175
|
<div className="str-video__speaker-layout__wrapper">
|
|
163
|
-
<ParticipantsAudio participants={remoteParticipants} />
|
|
176
|
+
{!muted && <ParticipantsAudio participants={remoteParticipants} />}
|
|
164
177
|
<div
|
|
165
178
|
className={clsx(
|
|
166
179
|
'str-video__speaker-layout',
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
combineComparators,
|
|
6
6
|
Comparator,
|
|
7
7
|
defaultSortPreset,
|
|
8
|
+
hasAudio,
|
|
9
|
+
hasVideo,
|
|
8
10
|
isPinned,
|
|
9
11
|
paginatedLayoutSortPreset,
|
|
10
12
|
screenSharing,
|
|
@@ -17,7 +19,11 @@ import { applyFilter, Filter } from '../../../utilities/filter';
|
|
|
17
19
|
export type FilterableParticipant = Pick<
|
|
18
20
|
StreamVideoParticipant,
|
|
19
21
|
'userId' | 'isSpeaking' | 'isDominantSpeaker' | 'name' | 'roles'
|
|
20
|
-
> & {
|
|
22
|
+
> & {
|
|
23
|
+
isPinned: boolean;
|
|
24
|
+
hasVideo: boolean;
|
|
25
|
+
hasAudio: boolean;
|
|
26
|
+
};
|
|
21
27
|
|
|
22
28
|
export type ParticipantFilter = Filter<FilterableParticipant>;
|
|
23
29
|
export type ParticipantPredicate = (
|
|
@@ -66,6 +72,8 @@ export const applyParticipantsFilter = (
|
|
|
66
72
|
name: participant.name,
|
|
67
73
|
roles: participant.roles,
|
|
68
74
|
isPinned: isPinned(participant),
|
|
75
|
+
hasVideo: hasVideo(participant),
|
|
76
|
+
hasAudio: hasAudio(participant),
|
|
69
77
|
},
|
|
70
78
|
filter,
|
|
71
79
|
);
|