agora-appbuilder-core 4.0.0-api.1 → 4.0.0-api.3
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/package.json +1 -1
- package/template/customization-api/sub-components.ts +8 -2
- package/template/customization-api/typeDefinition.ts +6 -4
- package/template/customization-api/utils.ts +3 -0
- package/template/src/atoms/ToolbarMenu.tsx +40 -0
- package/template/src/atoms/ToolbarMenuItem.tsx +104 -0
- package/template/src/atoms/ToolbarPreset.tsx +39 -0
- package/template/src/components/Controls.tsx +248 -110
- package/template/src/components/Leftbar.tsx +108 -0
- package/template/src/components/Navbar.tsx +204 -79
- package/template/src/components/Rightbar.tsx +110 -0
- package/template/src/components/Settings.tsx +8 -1
- package/template/src/pages/video-call/VideoCallScreen.tsx +60 -13
- package/template/src/pages/video-call/index.ts +33 -29
- package/template/src/subComponents/CopyJoinInfo.tsx +5 -0
- package/template/src/subComponents/LocalAudioMute.tsx +6 -0
- package/template/src/subComponents/LocalEndCall.tsx +9 -1
- package/template/src/subComponents/LocalSwitchCamera.tsx +5 -0
- package/template/src/subComponents/LocalVideoMute.tsx +6 -0
- package/template/src/subComponents/Recording.tsx +6 -3
- package/template/src/subComponents/livestream/controls/LocalRaiseHand.tsx +31 -24
- package/template/src/subComponents/screenshare/ScreenshareButton.tsx +5 -0
- package/template/src/utils/useMenu.tsx +31 -0
package/package.json
CHANGED
|
@@ -18,8 +18,6 @@ export {Icons} from '../agora-rn-uikit';
|
|
|
18
18
|
//video call components
|
|
19
19
|
export {
|
|
20
20
|
ParticipantsView,
|
|
21
|
-
Controls,
|
|
22
|
-
Navbar,
|
|
23
21
|
ChatBubble,
|
|
24
22
|
ChatInput,
|
|
25
23
|
Chat,
|
|
@@ -52,3 +50,11 @@ export {
|
|
|
52
50
|
ClientRole as UikitClientRole,
|
|
53
51
|
ChannelProfile as UikitChannelProfile,
|
|
54
52
|
} from '../agora-rn-uikit';
|
|
53
|
+
export {default as Toolbar} from '../src/atoms/Toolbar';
|
|
54
|
+
export {default as ToolbarItem} from '../src/atoms/ToolbarItem';
|
|
55
|
+
export {default as ToolbarPreset} from '../src/atoms/ToolbarPreset';
|
|
56
|
+
export {default as ToolbarMenu} from '../src/atoms/ToolbarMenu';
|
|
57
|
+
export type {
|
|
58
|
+
ToolbarCustomItem,
|
|
59
|
+
ToolbarPresetProps,
|
|
60
|
+
} from '../src/atoms/ToolbarPreset';
|
|
@@ -22,6 +22,7 @@ import {IconsInterface} from '../src/atoms/CustomIcon';
|
|
|
22
22
|
export type {ContentInterface, ContentStateInterface, UidType};
|
|
23
23
|
export type {ChatTextInputProps} from '../src/subComponents/ChatInput';
|
|
24
24
|
import {ChatTextInputProps} from '../src/subComponents/ChatInput';
|
|
25
|
+
import {ToolbarCustomItem} from './sub-components';
|
|
25
26
|
|
|
26
27
|
export const CUSTOM_ROUTES_PREFIX = '/r/';
|
|
27
28
|
|
|
@@ -59,12 +60,13 @@ export interface layoutItem {
|
|
|
59
60
|
component: layoutComponent;
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
export type ToolbarType = React.ComponentType | Array<ToolbarCustomItem>;
|
|
62
64
|
export interface VideoCallInterface extends BeforeAndAfterInterface {
|
|
63
65
|
// commented for v1 release
|
|
64
|
-
topToolBar?:
|
|
65
|
-
bottomToolBar?:
|
|
66
|
-
leftToolBar?:
|
|
67
|
-
rightToolBar?:
|
|
66
|
+
topToolBar?: ToolbarType;
|
|
67
|
+
bottomToolBar?: ToolbarType;
|
|
68
|
+
leftToolBar?: ToolbarType;
|
|
69
|
+
rightToolBar?: ToolbarType;
|
|
68
70
|
//settingsPanel?: React.ComponentType;
|
|
69
71
|
participantsPanel?: React.ComponentType;
|
|
70
72
|
chat?: ChatCmpInterface;
|
|
@@ -21,6 +21,9 @@ export {default as useIsPSTN} from '../src/utils/useIsPSTN';
|
|
|
21
21
|
export {default as useIsAudioEnabled} from '../src/utils/useIsAudioEnabled';
|
|
22
22
|
export {default as useIsVideoEnabled} from '../src/utils/useIsVideoEnabled';
|
|
23
23
|
|
|
24
|
+
//hook to get active speaker uid
|
|
25
|
+
export {default as useActiveSpeaker} from '../src/utils/useActiveSpeaker';
|
|
26
|
+
|
|
24
27
|
//hooks used for navigation
|
|
25
28
|
export {useHistory, useParams} from '../src/components/Router';
|
|
26
29
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View, StyleSheet, ViewStyle} from 'react-native';
|
|
3
|
+
import {ToolbarMenuProvider} from '../utils/useMenu';
|
|
4
|
+
|
|
5
|
+
export interface ToolbarMenuProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
containerStyle?: ViewStyle;
|
|
8
|
+
}
|
|
9
|
+
const ToolbarMenu = (props: ToolbarMenuProps) => {
|
|
10
|
+
const {children, containerStyle} = props;
|
|
11
|
+
if (!children) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return (
|
|
15
|
+
<ToolbarMenuProvider>
|
|
16
|
+
<View style={[ToolbarMenuStyle.containerStyle, containerStyle]}>
|
|
17
|
+
{children}
|
|
18
|
+
</View>
|
|
19
|
+
</ToolbarMenuProvider>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
export default ToolbarMenu;
|
|
23
|
+
|
|
24
|
+
const ToolbarMenuStyle = StyleSheet.create({
|
|
25
|
+
containerStyle: {
|
|
26
|
+
flex: 1,
|
|
27
|
+
justifyContent: 'flex-start',
|
|
28
|
+
backgroundColor: $config.CARD_LAYER_4_COLOR,
|
|
29
|
+
borderRadius: 4,
|
|
30
|
+
shadowColor: '#000',
|
|
31
|
+
shadowOffset: {
|
|
32
|
+
width: 0,
|
|
33
|
+
height: 4,
|
|
34
|
+
},
|
|
35
|
+
shadowOpacity: 0.1,
|
|
36
|
+
shadowRadius: 8,
|
|
37
|
+
zIndex: 1,
|
|
38
|
+
elevation: 1,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, {useState} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TouchableOpacityProps,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
Text,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import ThemeConfig from '../theme';
|
|
10
|
+
import ImageIcon from './ImageIcon';
|
|
11
|
+
import {IconButtonProps} from './IconButton';
|
|
12
|
+
import {isWebInternal} from '../utils/common';
|
|
13
|
+
import hexadecimalTransparency from '../utils/hexadecimalTransparency';
|
|
14
|
+
|
|
15
|
+
export interface ToolbarMenuItemProps extends IconButtonProps {
|
|
16
|
+
// disabled?: boolean;
|
|
17
|
+
// onPress: TouchableOpacityProps['onPress'];
|
|
18
|
+
// isHovered?: boolean;
|
|
19
|
+
// menuIcon?: string;
|
|
20
|
+
// menuText?: string;
|
|
21
|
+
// menuIconColor?: string;
|
|
22
|
+
}
|
|
23
|
+
const ToolbarMenuItem = (props: ToolbarMenuItemProps) => {
|
|
24
|
+
return (
|
|
25
|
+
<PlatformWrapper>
|
|
26
|
+
{(isHovered: boolean) => (
|
|
27
|
+
<>
|
|
28
|
+
<TouchableOpacity
|
|
29
|
+
disabled={props.disabled}
|
|
30
|
+
style={[
|
|
31
|
+
props.disabled ? {opacity: 0.4} : {},
|
|
32
|
+
isHovered
|
|
33
|
+
? {
|
|
34
|
+
backgroundColor:
|
|
35
|
+
$config.CARD_LAYER_5_COLOR +
|
|
36
|
+
hexadecimalTransparency['15%'],
|
|
37
|
+
}
|
|
38
|
+
: {},
|
|
39
|
+
ToolbarMenuItemStyles.container,
|
|
40
|
+
]}
|
|
41
|
+
onPress={props.onPress}>
|
|
42
|
+
<View style={ToolbarMenuItemStyles.iconContainer}>
|
|
43
|
+
<ImageIcon
|
|
44
|
+
base64={false}
|
|
45
|
+
iconType="plain"
|
|
46
|
+
iconSize={20}
|
|
47
|
+
name={props?.iconProps?.name}
|
|
48
|
+
tintColor={props?.iconProps?.tintColor}
|
|
49
|
+
/>
|
|
50
|
+
</View>
|
|
51
|
+
<Text
|
|
52
|
+
style={[
|
|
53
|
+
ToolbarMenuItemStyles.textStyle,
|
|
54
|
+
props?.btnTextProps?.textStyle,
|
|
55
|
+
props?.btnTextProps?.textColor
|
|
56
|
+
? {color: props?.btnTextProps?.textColor}
|
|
57
|
+
: {},
|
|
58
|
+
]}>
|
|
59
|
+
{props?.btnTextProps?.text}
|
|
60
|
+
</Text>
|
|
61
|
+
</TouchableOpacity>
|
|
62
|
+
</>
|
|
63
|
+
)}
|
|
64
|
+
</PlatformWrapper>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
export default ToolbarMenuItem;
|
|
68
|
+
const PlatformWrapper = ({children}) => {
|
|
69
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
70
|
+
return isWebInternal() ? (
|
|
71
|
+
<div
|
|
72
|
+
onMouseEnter={() => {
|
|
73
|
+
setIsHovered(true);
|
|
74
|
+
}}
|
|
75
|
+
onMouseLeave={() => {
|
|
76
|
+
setIsHovered(false);
|
|
77
|
+
}}>
|
|
78
|
+
{children(isHovered)}
|
|
79
|
+
</div>
|
|
80
|
+
) : (
|
|
81
|
+
<>{children(false)}</>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
const ToolbarMenuItemStyles = StyleSheet.create({
|
|
85
|
+
container: {
|
|
86
|
+
flexDirection: 'row',
|
|
87
|
+
borderBottomWidth: 1,
|
|
88
|
+
borderBottomColor: $config.CARD_LAYER_3_COLOR,
|
|
89
|
+
},
|
|
90
|
+
iconContainer: {
|
|
91
|
+
justifyContent: 'center',
|
|
92
|
+
alignItems: 'center',
|
|
93
|
+
marginRight: 8,
|
|
94
|
+
marginVertical: 12,
|
|
95
|
+
marginLeft: 12,
|
|
96
|
+
},
|
|
97
|
+
textStyle: {
|
|
98
|
+
paddingVertical: 14,
|
|
99
|
+
color: $config.SECONDARY_ACTION_COLOR,
|
|
100
|
+
fontSize: ThemeConfig.FontSize.normal,
|
|
101
|
+
fontWeight: '400',
|
|
102
|
+
fontFamily: ThemeConfig.FontFamily.sansPro,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Navbar from '../components/Navbar';
|
|
3
|
+
import Controls from '../components/Controls';
|
|
4
|
+
import Leftbar from '../components/Leftbar';
|
|
5
|
+
import Rightbar from '../components/Rightbar';
|
|
6
|
+
|
|
7
|
+
export interface ToolbarCustomItem {
|
|
8
|
+
component: () => JSX.Element;
|
|
9
|
+
align: 'start' | 'center' | 'end';
|
|
10
|
+
hide: 'yes' | 'no' | 'never';
|
|
11
|
+
order?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ToolbarPresetProps {
|
|
15
|
+
align: 'top' | 'left' | 'bottom' | 'right';
|
|
16
|
+
customItems?: Array<ToolbarCustomItem>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ToolbarPreset = (props: ToolbarPresetProps) => {
|
|
20
|
+
const {align} = props;
|
|
21
|
+
if (!align) {
|
|
22
|
+
console.log(
|
|
23
|
+
'ToolbarPreset align prop is empty. please provide align prop to get the corresponding toolbar preset',
|
|
24
|
+
);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (align === 'left') {
|
|
28
|
+
return <Leftbar customItems={props?.customItems} />;
|
|
29
|
+
} else if (align === 'right') {
|
|
30
|
+
return <Rightbar customItems={props?.customItems} />;
|
|
31
|
+
} else if (align === 'top') {
|
|
32
|
+
return <Navbar customItems={props?.customItems} />;
|
|
33
|
+
} else if (align === 'bottom') {
|
|
34
|
+
return <Controls customItems={props?.customItems} />;
|
|
35
|
+
} else {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export default ToolbarPreset;
|
|
@@ -10,38 +10,21 @@
|
|
|
10
10
|
*********************************************
|
|
11
11
|
*/
|
|
12
12
|
import React, {useState, useContext, useEffect, useRef} from 'react';
|
|
13
|
-
import {View, StyleSheet,
|
|
13
|
+
import {View, StyleSheet, useWindowDimensions} from 'react-native';
|
|
14
14
|
import {PropsContext} from '../../agora-rn-uikit';
|
|
15
|
-
import LocalAudioMute
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
} from '../subComponents/LocalVideoMute';
|
|
21
|
-
import Recording, {RecordingButtonProps} from '../subComponents/Recording';
|
|
22
|
-
import LocalSwitchCamera, {
|
|
23
|
-
LocalSwitchCameraProps,
|
|
24
|
-
} from '../subComponents/LocalSwitchCamera';
|
|
25
|
-
import ScreenshareButton, {
|
|
26
|
-
ScreenshareButtonProps,
|
|
27
|
-
} from '../subComponents/screenshare/ScreenshareButton';
|
|
15
|
+
import LocalAudioMute from '../subComponents/LocalAudioMute';
|
|
16
|
+
import LocalVideoMute from '../subComponents/LocalVideoMute';
|
|
17
|
+
import Recording from '../subComponents/Recording';
|
|
18
|
+
import LocalSwitchCamera from '../subComponents/LocalSwitchCamera';
|
|
19
|
+
import ScreenshareButton from '../subComponents/screenshare/ScreenshareButton';
|
|
28
20
|
import isMobileOrTablet from '../utils/isMobileOrTablet';
|
|
29
21
|
import {ClientRole} from '../../agora-rn-uikit';
|
|
30
|
-
import LiveStreamControls
|
|
31
|
-
|
|
32
|
-
} from './livestream/views/LiveStreamControls';
|
|
33
|
-
import {
|
|
34
|
-
BREAKPOINTS,
|
|
35
|
-
calculatePosition,
|
|
36
|
-
isWebInternal,
|
|
37
|
-
useIsDesktop,
|
|
38
|
-
} from '../utils/common';
|
|
22
|
+
import LiveStreamControls from './livestream/views/LiveStreamControls';
|
|
23
|
+
import {BREAKPOINTS, useIsDesktop} from '../utils/common';
|
|
39
24
|
import {useRoomInfo} from './room-info/useRoomInfo';
|
|
40
|
-
import LocalEndcall
|
|
41
|
-
import Spacer from '../atoms/Spacer';
|
|
25
|
+
import LocalEndcall from '../subComponents/LocalEndCall';
|
|
42
26
|
import LayoutIconButton from '../subComponents/LayoutIconButton';
|
|
43
27
|
import CopyJoinInfo from '../subComponents/CopyJoinInfo';
|
|
44
|
-
import hexadecimalTransparency from '../utils/hexadecimalTransparency';
|
|
45
28
|
import IconButton from '../atoms/IconButton';
|
|
46
29
|
import ActionMenu, {ActionMenuItem} from '../atoms/ActionMenu';
|
|
47
30
|
import useLayoutsData from '../pages/video-call/useLayoutsData';
|
|
@@ -57,8 +40,8 @@ import {useVideoCall} from './useVideoCall';
|
|
|
57
40
|
import {useScreenshare} from '../subComponents/screenshare/useScreenshare';
|
|
58
41
|
import LayoutIconDropdown from '../subComponents/LayoutIconDropdown';
|
|
59
42
|
import Toolbar from '../atoms/Toolbar';
|
|
60
|
-
import {ToolbarPosition, useToolbar} from '../utils/useToolbar';
|
|
61
43
|
import ToolbarItem from '../atoms/ToolbarItem';
|
|
44
|
+
import {ToolbarCustomItem} from '../atoms/ToolbarPreset';
|
|
62
45
|
|
|
63
46
|
const MoreButton = () => {
|
|
64
47
|
const {rtcProps} = useContext(PropsContext);
|
|
@@ -278,117 +261,272 @@ const MoreButton = () => {
|
|
|
278
261
|
</>
|
|
279
262
|
);
|
|
280
263
|
};
|
|
281
|
-
const
|
|
264
|
+
export const LayoutToolbarItem = () => (
|
|
265
|
+
<ToolbarItem testID="layout-btn" collapsable={false}>
|
|
266
|
+
{/**
|
|
267
|
+
* .measure returns undefined on Android unless collapsable=false or onLayout are specified
|
|
268
|
+
* so added collapsable property
|
|
269
|
+
* https://github.com/facebook/react-native/issues/29712
|
|
270
|
+
* */}
|
|
271
|
+
<LayoutIconButton />
|
|
272
|
+
</ToolbarItem>
|
|
273
|
+
);
|
|
274
|
+
export const InviteToolbarItem = () => {
|
|
275
|
+
return (
|
|
276
|
+
<ToolbarItem testID="invite-btn">
|
|
277
|
+
<CopyJoinInfo />
|
|
278
|
+
</ToolbarItem>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
const defaultStartItems: Array<ToolbarCustomItem> = [
|
|
282
|
+
{
|
|
283
|
+
align: 'start',
|
|
284
|
+
component: LayoutToolbarItem,
|
|
285
|
+
order: 0,
|
|
286
|
+
hide: 'no',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
align: 'start',
|
|
290
|
+
component: InviteToolbarItem,
|
|
291
|
+
order: 1,
|
|
292
|
+
hide: 'no',
|
|
293
|
+
},
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
export const RaiseHandToolbarItem = () => {
|
|
282
297
|
const {rtcProps} = useContext(PropsContext);
|
|
283
298
|
const isDesktop = useIsDesktop();
|
|
284
299
|
const {
|
|
285
300
|
data: {isHost},
|
|
286
301
|
} = useRoomInfo();
|
|
287
|
-
|
|
302
|
+
return $config.EVENT_MODE ? (
|
|
303
|
+
rtcProps.role == ClientRole.Audience ? (
|
|
304
|
+
<LiveStreamControls
|
|
305
|
+
showControls={true}
|
|
306
|
+
isDesktop={isDesktop('toolbar')}
|
|
307
|
+
/>
|
|
308
|
+
) : rtcProps?.role == ClientRole.Broadcaster ? (
|
|
309
|
+
/**
|
|
310
|
+
* In event mode when raise hand feature is active
|
|
311
|
+
* and audience is promoted to host, the audience can also
|
|
312
|
+
* demote himself
|
|
313
|
+
*/
|
|
314
|
+
<LiveStreamControls
|
|
315
|
+
isDesktop={isDesktop('toolbar')}
|
|
316
|
+
showControls={!isHost}
|
|
317
|
+
/>
|
|
318
|
+
) : (
|
|
319
|
+
<></>
|
|
320
|
+
)
|
|
321
|
+
) : (
|
|
322
|
+
<></>
|
|
323
|
+
);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export const LocalAudioToolbarItem = () => {
|
|
327
|
+
return (
|
|
328
|
+
<ToolbarItem testID="localAudio-btn">
|
|
329
|
+
<LocalAudioMute showToolTip={true} />
|
|
330
|
+
</ToolbarItem>
|
|
331
|
+
);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export const LocalVideoToolbarItem = () => {
|
|
335
|
+
return (
|
|
336
|
+
!$config.AUDIO_ROOM && (
|
|
337
|
+
<ToolbarItem testID="localVideo-btn">
|
|
338
|
+
<LocalVideoMute showToolTip={true} />
|
|
339
|
+
</ToolbarItem>
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export const SwitchCameraToolbarItem = () => {
|
|
345
|
+
return (
|
|
346
|
+
!$config.AUDIO_ROOM &&
|
|
347
|
+
isMobileOrTablet() && (
|
|
348
|
+
<ToolbarItem testID="switchCamera-btn">
|
|
349
|
+
<LocalSwitchCamera />
|
|
350
|
+
</ToolbarItem>
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
export const ScreenShareToolbarItem = () => {
|
|
356
|
+
const {width} = useWindowDimensions();
|
|
357
|
+
return (
|
|
358
|
+
width > BREAKPOINTS.sm &&
|
|
359
|
+
$config.SCREEN_SHARING &&
|
|
360
|
+
!isMobileOrTablet() && (
|
|
361
|
+
<ToolbarItem testID="screenShare-btn">
|
|
362
|
+
<ScreenshareButton />
|
|
363
|
+
</ToolbarItem>
|
|
364
|
+
)
|
|
365
|
+
);
|
|
366
|
+
};
|
|
367
|
+
export const RecordingToolbarItem = () => {
|
|
368
|
+
const {width} = useWindowDimensions();
|
|
369
|
+
const {
|
|
370
|
+
data: {isHost},
|
|
371
|
+
} = useRoomInfo();
|
|
372
|
+
return (
|
|
373
|
+
width > BREAKPOINTS.sm &&
|
|
374
|
+
isHost &&
|
|
375
|
+
$config.CLOUD_RECORDING && (
|
|
376
|
+
<ToolbarItem testID="recording-btn">
|
|
377
|
+
<Recording />
|
|
378
|
+
</ToolbarItem>
|
|
379
|
+
)
|
|
380
|
+
);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
export const MoreButtonToolbarItem = () => {
|
|
288
384
|
const {width} = useWindowDimensions();
|
|
385
|
+
return (
|
|
386
|
+
width < BREAKPOINTS.md && (
|
|
387
|
+
<ToolbarItem testID="more-btn">
|
|
388
|
+
<MoreButton />
|
|
389
|
+
</ToolbarItem>
|
|
390
|
+
)
|
|
391
|
+
);
|
|
392
|
+
};
|
|
393
|
+
export const LocalEndcallToolbarItem = () => {
|
|
394
|
+
return (
|
|
395
|
+
<ToolbarItem testID="endCall-btn">
|
|
396
|
+
<LocalEndcall />
|
|
397
|
+
</ToolbarItem>
|
|
398
|
+
);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const defaultCenterItems: ToolbarCustomItem[] = [
|
|
402
|
+
{
|
|
403
|
+
align: 'start',
|
|
404
|
+
component: RaiseHandToolbarItem,
|
|
405
|
+
order: 0,
|
|
406
|
+
hide: 'no',
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
align: 'start',
|
|
410
|
+
component: LocalAudioToolbarItem,
|
|
411
|
+
order: 1,
|
|
412
|
+
hide: 'no',
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
align: 'start',
|
|
416
|
+
component: LocalVideoToolbarItem,
|
|
417
|
+
order: 2,
|
|
418
|
+
hide: 'no',
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
align: 'start',
|
|
422
|
+
component: SwitchCameraToolbarItem,
|
|
423
|
+
order: 3,
|
|
424
|
+
hide: 'no',
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
align: 'start',
|
|
428
|
+
component: ScreenShareToolbarItem,
|
|
429
|
+
order: 4,
|
|
430
|
+
hide: 'no',
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
align: 'start',
|
|
434
|
+
component: RecordingToolbarItem,
|
|
435
|
+
order: 5,
|
|
436
|
+
hide: 'no',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
align: 'start',
|
|
440
|
+
component: MoreButtonToolbarItem,
|
|
441
|
+
order: 6,
|
|
442
|
+
hide: 'no',
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
align: 'start',
|
|
446
|
+
component: LocalEndcallToolbarItem,
|
|
447
|
+
order: 7,
|
|
448
|
+
hide: 'no',
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
const defaultEndItems: ToolbarCustomItem[] = [];
|
|
453
|
+
|
|
454
|
+
export interface ControlsProps {
|
|
455
|
+
customItems?: ToolbarCustomItem[];
|
|
456
|
+
}
|
|
457
|
+
const Controls = (props: ControlsProps) => {
|
|
458
|
+
const {customItems = []} = props;
|
|
459
|
+
const {width} = useWindowDimensions();
|
|
460
|
+
|
|
461
|
+
const isHidden = (i) => {
|
|
462
|
+
return i?.hide === 'yes';
|
|
463
|
+
};
|
|
464
|
+
const customStartItems = customItems
|
|
465
|
+
?.filter((i) => i?.align === 'start' && !isHidden(i))
|
|
466
|
+
?.concat(defaultStartItems)
|
|
467
|
+
?.sort((a, b) => a?.order - b?.order);
|
|
468
|
+
|
|
469
|
+
const customCenterItems = customItems
|
|
470
|
+
?.filter((i) => i?.align === 'center' && !isHidden(i))
|
|
471
|
+
?.concat(defaultCenterItems)
|
|
472
|
+
?.sort((a, b) => a?.order - b?.order);
|
|
473
|
+
|
|
474
|
+
const customEndItems = customItems
|
|
475
|
+
?.filter((i) => i?.align === 'end' && !isHidden(i))
|
|
476
|
+
?.concat(defaultEndItems)
|
|
477
|
+
?.sort((a, b) => a?.order - b?.order);
|
|
478
|
+
|
|
479
|
+
const renderContent = (
|
|
480
|
+
items: ToolbarCustomItem[],
|
|
481
|
+
type: 'start' | 'center' | 'end',
|
|
482
|
+
) => {
|
|
483
|
+
return items?.map((item, index) => {
|
|
484
|
+
const ToolbarItem = item?.component;
|
|
485
|
+
if (ToolbarItem) {
|
|
486
|
+
return <ToolbarItem key={`bottom-toolbar-${type}` + index} />;
|
|
487
|
+
} else {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
};
|
|
289
492
|
return (
|
|
290
493
|
<Toolbar>
|
|
291
494
|
{width >= BREAKPOINTS.md && (
|
|
292
|
-
<View
|
|
293
|
-
|
|
294
|
-
style.leftContent,
|
|
295
|
-
isHorizontal ? {flexDirection: 'row'} : {flexDirection: 'column'},
|
|
296
|
-
]}>
|
|
297
|
-
<ToolbarItem testID="layout-btn" collapsable={false}>
|
|
298
|
-
{/**
|
|
299
|
-
* .measure returns undefined on Android unless collapsable=false or onLayout are specified
|
|
300
|
-
* so added collapsable property
|
|
301
|
-
* https://github.com/facebook/react-native/issues/29712
|
|
302
|
-
* */}
|
|
303
|
-
<LayoutIconButton />
|
|
304
|
-
</ToolbarItem>
|
|
305
|
-
<ToolbarItem testID="invite-btn">
|
|
306
|
-
<CopyJoinInfo />
|
|
307
|
-
</ToolbarItem>
|
|
495
|
+
<View style={[style.startContent]}>
|
|
496
|
+
{renderContent(customStartItems, 'start')}
|
|
308
497
|
</View>
|
|
309
498
|
)}
|
|
310
|
-
<View
|
|
311
|
-
|
|
312
|
-
style.centerContent,
|
|
313
|
-
isHorizontal ? {flexDirection: 'row'} : {flexDirection: 'column'},
|
|
314
|
-
]}>
|
|
315
|
-
{$config.EVENT_MODE && rtcProps.role == ClientRole.Audience ? (
|
|
316
|
-
<LiveStreamControls
|
|
317
|
-
showControls={true}
|
|
318
|
-
isDesktop={isDesktop('toolbar')}
|
|
319
|
-
/>
|
|
320
|
-
) : (
|
|
321
|
-
<></>
|
|
322
|
-
)}
|
|
323
|
-
<>
|
|
324
|
-
{/**
|
|
325
|
-
* In event mode when raise hand feature is active
|
|
326
|
-
* and audience is promoted to host, the audience can also
|
|
327
|
-
* demote himself
|
|
328
|
-
*/}
|
|
329
|
-
{$config.EVENT_MODE ? (
|
|
330
|
-
<LiveStreamControls
|
|
331
|
-
isDesktop={isDesktop('toolbar')}
|
|
332
|
-
showControls={rtcProps?.role == ClientRole.Broadcaster && !isHost}
|
|
333
|
-
/>
|
|
334
|
-
) : (
|
|
335
|
-
<></>
|
|
336
|
-
)}
|
|
337
|
-
<ToolbarItem testID="localAudio-btn">
|
|
338
|
-
<LocalAudioMute showToolTip={true} />
|
|
339
|
-
</ToolbarItem>
|
|
340
|
-
{!$config.AUDIO_ROOM && (
|
|
341
|
-
<ToolbarItem testID="localVideo-btn">
|
|
342
|
-
<LocalVideoMute showToolTip={true} />
|
|
343
|
-
</ToolbarItem>
|
|
344
|
-
)}
|
|
345
|
-
{!$config.AUDIO_ROOM && isMobileOrTablet() && (
|
|
346
|
-
<ToolbarItem testID="switchCamera-btn">
|
|
347
|
-
<LocalSwitchCamera />
|
|
348
|
-
</ToolbarItem>
|
|
349
|
-
)}
|
|
350
|
-
{width > BREAKPOINTS.sm &&
|
|
351
|
-
$config.SCREEN_SHARING &&
|
|
352
|
-
!isMobileOrTablet() && (
|
|
353
|
-
<ToolbarItem testID="screenShare-btn">
|
|
354
|
-
<ScreenshareButton />
|
|
355
|
-
</ToolbarItem>
|
|
356
|
-
)}
|
|
357
|
-
{width > BREAKPOINTS.sm && isHost && $config.CLOUD_RECORDING && (
|
|
358
|
-
<ToolbarItem testID="recording-btn">
|
|
359
|
-
<Recording />
|
|
360
|
-
</ToolbarItem>
|
|
361
|
-
)}
|
|
362
|
-
</>
|
|
363
|
-
{width < BREAKPOINTS.md && (
|
|
364
|
-
<ToolbarItem testID="more-btn">
|
|
365
|
-
<MoreButton />
|
|
366
|
-
</ToolbarItem>
|
|
367
|
-
)}
|
|
368
|
-
<ToolbarItem testID="endCall-btn">
|
|
369
|
-
<LocalEndcall />
|
|
370
|
-
</ToolbarItem>
|
|
499
|
+
<View style={[style.centerContent]}>
|
|
500
|
+
{renderContent(customCenterItems, 'center')}
|
|
371
501
|
</View>
|
|
372
|
-
{width >= BREAKPOINTS.md &&
|
|
502
|
+
{width >= BREAKPOINTS.md && (
|
|
503
|
+
<View style={style.endContent}>
|
|
504
|
+
{renderContent(customEndItems, 'end')}
|
|
505
|
+
</View>
|
|
506
|
+
)}
|
|
373
507
|
</Toolbar>
|
|
374
508
|
);
|
|
375
509
|
};
|
|
376
510
|
|
|
377
511
|
const style = StyleSheet.create({
|
|
378
|
-
|
|
512
|
+
startContent: {
|
|
379
513
|
flex: 1,
|
|
380
|
-
|
|
514
|
+
flexDirection: 'row',
|
|
381
515
|
justifyContent: 'flex-start',
|
|
382
516
|
alignItems: 'center',
|
|
383
517
|
},
|
|
384
518
|
centerContent: {
|
|
385
519
|
zIndex: 2,
|
|
386
520
|
flex: 1,
|
|
521
|
+
flexDirection: 'row',
|
|
387
522
|
justifyContent: 'center',
|
|
388
523
|
alignItems: 'center',
|
|
389
524
|
},
|
|
390
|
-
|
|
525
|
+
endContent: {
|
|
391
526
|
flex: 1,
|
|
527
|
+
flexDirection: 'row',
|
|
528
|
+
justifyContent: 'flex-end',
|
|
529
|
+
alignItems: 'center',
|
|
392
530
|
},
|
|
393
531
|
});
|
|
394
532
|
|