agora-appbuilder-core 4.0.0-api.1 → 4.0.0-api.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.0-api.1",
3
+ "version": "4.0.0-api.2",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -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,10 @@ 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 type {
57
+ ToolbarCustomItem,
58
+ ToolbarPresetProps,
59
+ } from '../src/atoms/ToolbarPreset';
@@ -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,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, Text, useWindowDimensions} from 'react-native';
13
+ import {View, StyleSheet, useWindowDimensions} from 'react-native';
14
14
  import {PropsContext} from '../../agora-rn-uikit';
15
- import LocalAudioMute, {
16
- LocalAudioMuteProps,
17
- } from '../subComponents/LocalAudioMute';
18
- import LocalVideoMute, {
19
- LocalVideoMuteProps,
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
- LiveStreamControlsProps,
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, {LocalEndcallProps} from '../subComponents/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 'src/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 Controls = () => {
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
- const {isHorizontal} = useToolbar();
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
- style={[
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
- style={[
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 && <View style={style.rightContent}></View>}
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
- leftContent: {
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
- rightContent: {
525
+ endContent: {
391
526
  flex: 1,
527
+ flexDirection: 'row',
528
+ justifyContent: 'flex-end',
529
+ alignItems: 'center',
392
530
  },
393
531
  });
394
532
 
@@ -0,0 +1,108 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+ import React from 'react';
13
+ import {View, StyleSheet} from 'react-native';
14
+ import Toolbar from '../atoms/Toolbar';
15
+ import {ToolbarCustomItem} from '../atoms/ToolbarPreset';
16
+
17
+ const defaultStartItems: ToolbarCustomItem[] = [];
18
+ const defaultCenterItems: ToolbarCustomItem[] = [];
19
+ const defaultEndItems: ToolbarCustomItem[] = [];
20
+
21
+ export interface LeftbarProps {
22
+ customItems?: ToolbarCustomItem[];
23
+ }
24
+ const Leftbar = (props: LeftbarProps) => {
25
+ const {customItems = []} = props;
26
+ const isHidden = (i) => {
27
+ return i?.hide === 'yes';
28
+ };
29
+ const customStartItems = customItems
30
+ ?.filter((i) => i?.align === 'start' && !isHidden(i))
31
+ ?.concat(defaultStartItems)
32
+ ?.sort((a, b) => a?.order - b?.order);
33
+
34
+ const customCenterItems = customItems
35
+ ?.filter((i) => i?.align === 'center' && !isHidden(i))
36
+ ?.concat(defaultCenterItems)
37
+ ?.sort((a, b) => a?.order - b?.order);
38
+
39
+ const customEndItems = customItems
40
+ ?.filter((i) => i?.align === 'end' && !isHidden(i))
41
+ ?.concat(defaultEndItems)
42
+ ?.sort((a, b) => a?.order - b?.order);
43
+
44
+ const renderContent = (
45
+ items: ToolbarCustomItem[],
46
+ type: 'start' | 'center' | 'end',
47
+ ) => {
48
+ return items?.map((item, index) => {
49
+ const ToolbarItem = item?.component;
50
+ if (ToolbarItem) {
51
+ return <ToolbarItem key={`left-toolbar-${type}` + index} />;
52
+ } else {
53
+ return null;
54
+ }
55
+ });
56
+ };
57
+ return (
58
+ <Toolbar>
59
+ <View style={style.startContent}>
60
+ {customStartItems && customStartItems?.length ? (
61
+ renderContent(customStartItems, 'start')
62
+ ) : (
63
+ <></>
64
+ )}
65
+ </View>
66
+ <View style={style.centerContent}>
67
+ {customCenterItems && customCenterItems?.length ? (
68
+ renderContent(customCenterItems, 'center')
69
+ ) : (
70
+ <></>
71
+ )}
72
+ </View>
73
+ <View style={style.endContent}>
74
+ {customEndItems && customEndItems?.length ? (
75
+ renderContent(customEndItems, 'end')
76
+ ) : (
77
+ <></>
78
+ )}
79
+ </View>
80
+ </Toolbar>
81
+ );
82
+ };
83
+
84
+ const style = StyleSheet.create({
85
+ startContent: {
86
+ flex: 1,
87
+ zIndex: 2,
88
+ flexDirection: 'column',
89
+ justifyContent: 'flex-start',
90
+ alignItems: 'center',
91
+ },
92
+ centerContent: {
93
+ flex: 1,
94
+ zIndex: 2,
95
+ flexDirection: 'column',
96
+ justifyContent: 'center',
97
+ alignItems: 'center',
98
+ },
99
+ endContent: {
100
+ flex: 1,
101
+ zIndex: 2,
102
+ flexDirection: 'column',
103
+ justifyContent: 'flex-end',
104
+ alignItems: 'center',
105
+ },
106
+ });
107
+
108
+ export default Leftbar;
@@ -60,6 +60,7 @@ import styles from 'react-native-toast-message/src/styles';
60
60
  import RecordingInfo from '../atoms/RecordingInfo';
61
61
  import Toolbar from '../atoms/Toolbar';
62
62
  import ToolbarItem from '../atoms/ToolbarItem';
63
+ import {ToolbarCustomItem} from '../atoms/ToolbarPreset';
63
64
 
64
65
  export const ParticipantsCountView = ({
65
66
  isMobileView = false,
@@ -312,72 +313,162 @@ const SettingsIconButtonWithWrapper = (props: SettingsIconButtonProps) => {
312
313
  return <SettingsWithViewWrapper {...props} />;
313
314
  };
314
315
 
315
- const Navbar = () => {
316
- //commented for v1 release
317
- //const recordingLabel = useString('recordingLabel')();
318
- const {isHorizontal} = useToolbar();
319
- const recordingLabel = 'Recording';
320
- const {audienceUids, hostUids} = useLiveStreamDataContext();
316
+ export const MeetingTitleToolbarItem = () => {
321
317
  const {
322
318
  data: {meetingTitle},
323
319
  } = useRoomInfo();
324
-
320
+ return (
321
+ <ToolbarItem>
322
+ <Text
323
+ style={style.roomNameText}
324
+ testID="videocall-meetingName"
325
+ numberOfLines={1}
326
+ ellipsizeMode="tail">
327
+ {trimText(meetingTitle)}
328
+ </Text>
329
+ </ToolbarItem>
330
+ );
331
+ };
332
+ export const ParticipantCountToolbarItem = () => {
333
+ return (
334
+ <ToolbarItem>
335
+ <ParticipantsCount />
336
+ </ToolbarItem>
337
+ );
338
+ };
339
+ export const RecordingStatusToolbarItem = () => {
340
+ const recordingLabel = 'Recording';
325
341
  const {isRecordingActive} = useRecording();
326
- const {onlineUsersCount} = useContext(ChatContext);
342
+ return isRecordingActive ? (
343
+ <ToolbarItem>
344
+ <RecordingInfo recordingLabel={recordingLabel} />
345
+ </ToolbarItem>
346
+ ) : (
347
+ <></>
348
+ );
349
+ };
350
+ const defaultStartItems: ToolbarCustomItem[] = [
351
+ {
352
+ align: 'start',
353
+ component: MeetingTitleToolbarItem,
354
+ order: 0,
355
+ hide: 'no',
356
+ },
357
+ {
358
+ align: 'start',
359
+ component: ParticipantCountToolbarItem,
360
+ order: 1,
361
+ hide: 'no',
362
+ },
363
+ {
364
+ align: 'start',
365
+ component: RecordingStatusToolbarItem,
366
+ order: 2,
367
+ hide: 'no',
368
+ },
369
+ ];
370
+ const defaultCenterItems: ToolbarCustomItem[] = [];
371
+
372
+ export const ParticipantToolbarItem = () => {
373
+ return (
374
+ <ToolbarItem testID="videocall-participantsicon">
375
+ <ParticipantsIconButton />
376
+ </ToolbarItem>
377
+ );
378
+ };
379
+
380
+ export const ChatToolbarItem = () => {
381
+ return (
382
+ $config.CHAT && (
383
+ <>
384
+ <ToolbarItem testID="videocall-chaticon">
385
+ <ChatIconButton />
386
+ </ToolbarItem>
387
+ </>
388
+ )
389
+ );
390
+ };
391
+ export const SettingsToobarItem = () => {
392
+ return (
393
+ <ToolbarItem testID="videocall-settingsicon">
394
+ <SettingsIconButtonWithWrapper />
395
+ </ToolbarItem>
396
+ );
397
+ };
398
+
399
+ const defaultEndItems: ToolbarCustomItem[] = [
400
+ {
401
+ align: 'start',
402
+ component: ParticipantToolbarItem,
403
+ order: 0,
404
+ hide: 'no',
405
+ },
406
+ {
407
+ align: 'start',
408
+ component: ChatToolbarItem,
409
+ order: 1,
410
+ hide: 'no',
411
+ },
412
+ {
413
+ align: 'start',
414
+ component: SettingsToobarItem,
415
+ order: 2,
416
+ hide: 'no',
417
+ },
418
+ ];
419
+
420
+ export interface NavbarProps {
421
+ customItems?: ToolbarCustomItem[];
422
+ }
423
+ const Navbar = (props: NavbarProps) => {
424
+ //commented for v1 release
425
+ //const recordingLabel = useString('recordingLabel')();
426
+ const {customItems = []} = props;
327
427
  const {width} = useWindowDimensions();
428
+
429
+ const isHidden = (i) => {
430
+ return i?.hide === 'yes';
431
+ };
432
+
433
+ const customStartItems = customItems
434
+ ?.filter((i) => i.align === 'start' && !isHidden(i))
435
+ ?.concat(defaultStartItems)
436
+ ?.sort((a, b) => a?.order - b?.order);
437
+
438
+ const customCenterItems = customItems
439
+ ?.filter((i) => i.align === 'center' && !isHidden(i))
440
+ ?.concat(defaultCenterItems)
441
+ ?.sort((a, b) => a?.order - b?.order);
442
+
443
+ const customEndItems = customItems
444
+ ?.filter((i) => i.align === 'end' && !isHidden(i))
445
+ ?.concat(defaultEndItems)
446
+ ?.sort((a, b) => a?.order - b?.order);
447
+
448
+ const renderContent = (
449
+ items: ToolbarCustomItem[],
450
+ type: 'start' | 'center' | 'end',
451
+ ) => {
452
+ return items?.map((item, index) => {
453
+ const ToolbarItem = item?.component;
454
+ if (ToolbarItem) {
455
+ return <ToolbarItem key={`top-toolbar-${type}` + index} />;
456
+ } else {
457
+ return null;
458
+ }
459
+ });
460
+ };
328
461
  return (
329
462
  <Toolbar>
330
- <View
331
- style={[
332
- isHorizontal
333
- ? {flexDirection: 'row'}
334
- : {flexDirection: 'column', justifyContent: 'center'},
335
- ]}>
336
- <ToolbarItem>
337
- <Text
338
- style={style.roomNameText}
339
- testID="videocall-meetingName"
340
- numberOfLines={1}
341
- ellipsizeMode="tail">
342
- {trimText(meetingTitle)}
343
- </Text>
344
- </ToolbarItem>
345
- {/* <Spacer size={8} horizontal={isHorizontal ? true : false} /> */}
346
- <ToolbarItem>
347
- <ParticipantsCount />
348
- </ToolbarItem>
349
- {isRecordingActive ? (
350
- <ToolbarItem>
351
- <RecordingInfo
352
- recordingLabel={isHorizontal ? recordingLabel : ''}
353
- />
354
- </ToolbarItem>
355
- ) : (
356
- <></>
357
- )}
463
+ <View style={style.startContent}>
464
+ {renderContent(customStartItems, 'start')}
465
+ </View>
466
+ <View style={style.centerContent}>
467
+ {renderContent(customCenterItems, 'center')}
358
468
  </View>
359
469
  {width > BREAKPOINTS.sm || isMobileUA() ? (
360
- <View
361
- style={[
362
- style.navControlBar,
363
- isHorizontal
364
- ? {flexDirection: 'row', width: '50%'}
365
- : {flexDirection: 'column'},
366
- ]}
367
- testID="videocall-navcontrols">
368
- <ToolbarItem testID="videocall-participantsicon">
369
- <ParticipantsIconButton />
370
- </ToolbarItem>
371
- {$config.CHAT && (
372
- <>
373
- <ToolbarItem testID="videocall-chaticon">
374
- <ChatIconButton />
375
- </ToolbarItem>
376
- </>
377
- )}
378
- <ToolbarItem testID="videocall-settingsicon">
379
- <SettingsIconButtonWithWrapper />
380
- </ToolbarItem>
470
+ <View style={style.endContent}>
471
+ {renderContent(customEndItems, 'end')}
381
472
  </View>
382
473
  ) : (
383
474
  <></>
@@ -387,6 +478,26 @@ const Navbar = () => {
387
478
  };
388
479
 
389
480
  const style = StyleSheet.create({
481
+ startContent: {
482
+ flex: 1,
483
+ flexDirection: 'row',
484
+ justifyContent: 'flex-start',
485
+ alignItems: 'center',
486
+ },
487
+ centerContent: {
488
+ zIndex: 2,
489
+ flex: 1,
490
+ flexDirection: 'row',
491
+ justifyContent: 'center',
492
+ alignItems: 'center',
493
+ },
494
+ endContent: {
495
+ flex: 1,
496
+ zIndex: 9,
497
+ flexDirection: 'row',
498
+ justifyContent: 'flex-end',
499
+ alignItems: 'center',
500
+ },
390
501
  participantCountView: {
391
502
  flexDirection: 'row',
392
503
  padding: 12,
@@ -440,10 +551,6 @@ const style = StyleSheet.create({
440
551
  fontSize: 12,
441
552
  color: $config.SECONDARY_ACTION_COLOR,
442
553
  },
443
- navControlBar: {
444
- justifyContent: 'flex-end',
445
- zIndex: 9,
446
- },
447
554
  navSmItem: {
448
555
  flexGrow: 0,
449
556
  flexShrink: 0,
@@ -0,0 +1,110 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+ import React from 'react';
13
+ import {View, StyleSheet} from 'react-native';
14
+ import Toolbar from '../atoms/Toolbar';
15
+ import {ToolbarCustomItem} from '../atoms/ToolbarPreset';
16
+
17
+ const defaultStartItems: ToolbarCustomItem[] = [];
18
+ const defaultCenterItems: ToolbarCustomItem[] = [];
19
+ const defaultEndItems: ToolbarCustomItem[] = [];
20
+
21
+ export interface RightbarProps {
22
+ customItems?: ToolbarCustomItem[];
23
+ }
24
+ const Rightbar = (props: RightbarProps) => {
25
+ const {customItems = []} = props;
26
+
27
+ const isHidden = (i) => {
28
+ return i?.hide === 'yes';
29
+ };
30
+
31
+ const customStartItems = customItems
32
+ ?.filter((i) => i?.align === 'start' && !isHidden(i))
33
+ ?.concat(defaultStartItems)
34
+ ?.sort((a, b) => a?.order - b?.order);
35
+
36
+ const customCenterItems = customItems
37
+ ?.filter((i) => i?.align === 'center' && !isHidden(i))
38
+ ?.concat(defaultCenterItems)
39
+ ?.sort((a, b) => a?.order - b?.order);
40
+
41
+ const customEndItems = customItems
42
+ ?.filter((i) => i?.align === 'end' && !isHidden(i))
43
+ ?.concat(defaultEndItems)
44
+ ?.sort((a, b) => a?.order - b?.order);
45
+
46
+ const renderContent = (
47
+ items: ToolbarCustomItem[],
48
+ type: 'start' | 'center' | 'end',
49
+ ) => {
50
+ return items?.map((item, index) => {
51
+ const ToolbarItem = item?.component;
52
+ if (ToolbarItem) {
53
+ return <ToolbarItem key={`left-toolbar-${type}` + index} />;
54
+ } else {
55
+ return null;
56
+ }
57
+ });
58
+ };
59
+ return (
60
+ <Toolbar>
61
+ <View style={style.startContent}>
62
+ {customStartItems && customStartItems?.length ? (
63
+ renderContent(customStartItems, 'start')
64
+ ) : (
65
+ <></>
66
+ )}
67
+ </View>
68
+ <View style={style.centerContent}>
69
+ {customCenterItems && customCenterItems?.length ? (
70
+ renderContent(customCenterItems, 'center')
71
+ ) : (
72
+ <></>
73
+ )}
74
+ </View>
75
+ <View style={style.endContent}>
76
+ {customEndItems && customEndItems?.length ? (
77
+ renderContent(customEndItems, 'end')
78
+ ) : (
79
+ <></>
80
+ )}
81
+ </View>
82
+ </Toolbar>
83
+ );
84
+ };
85
+
86
+ const style = StyleSheet.create({
87
+ startContent: {
88
+ flex: 1,
89
+ zIndex: 2,
90
+ flexDirection: 'column',
91
+ justifyContent: 'flex-start',
92
+ alignItems: 'center',
93
+ },
94
+ centerContent: {
95
+ flex: 1,
96
+ zIndex: 2,
97
+ flexDirection: 'column',
98
+ justifyContent: 'center',
99
+ alignItems: 'center',
100
+ },
101
+ endContent: {
102
+ flex: 1,
103
+ zIndex: 2,
104
+ flexDirection: 'column',
105
+ justifyContent: 'flex-end',
106
+ alignItems: 'center',
107
+ },
108
+ });
109
+
110
+ export default Rightbar;
@@ -7,10 +7,10 @@ import {
7
7
  Platform,
8
8
  } from 'react-native';
9
9
  import {useCustomization} from 'customization-implementation';
10
- import Navbar from '../../components/Navbar';
10
+ import Navbar, {NavbarProps} from '../../components/Navbar';
11
11
  import ParticipantsView from '../../components/ParticipantsView';
12
12
  import SettingsView from '../../components/SettingsView';
13
- import Controls from '../../components/Controls';
13
+ import Controls, {ControlsProps} from '../../components/Controls';
14
14
  import Chat, {ChatProps} from '../../components/Chat';
15
15
  import {SidePanelType} from '../../subComponents/SidePanelEnum';
16
16
  import {
@@ -28,6 +28,8 @@ import {useRoomInfo} from '../../components/room-info/useRoomInfo';
28
28
  import {controlMessageEnum, useUserName} from 'customization-api';
29
29
  import events, {PersistanceLevel} from '../../rtm-events-api';
30
30
  import VideoCallMobileView from './VideoCallMobileView';
31
+ import Leftbar, {LeftbarProps} from '../../components/Leftbar';
32
+ import Rightbar, {RightbarProps} from '../../components/Rightbar';
31
33
 
32
34
  const VideoCallScreen = () => {
33
35
  const {sidePanel} = useSidePanel();
@@ -50,14 +52,14 @@ const VideoCallScreen = () => {
50
52
  let components: {
51
53
  VideocallComponent?: React.ComponentType;
52
54
  ChatComponent: React.ComponentType<ChatProps>;
53
- BottombarComponent: React.ComponentType;
55
+ BottombarComponent: React.ComponentType<ControlsProps>;
54
56
  ParticipantsComponent: React.ComponentType;
55
57
  SettingsComponent: React.ComponentType;
56
- TopbarComponent: React.ComponentType;
58
+ TopbarComponent: React.ComponentType<NavbarProps>;
57
59
  VideocallBeforeView: React.ComponentType;
58
60
  VideocallAfterView: React.ComponentType;
59
- LeftbarComponent: React.ComponentType;
60
- RightbarComponent: React.ComponentType;
61
+ LeftbarComponent: React.ComponentType<LeftbarProps>;
62
+ RightbarComponent: React.ComponentType<RightbarProps>;
61
63
  } = {
62
64
  BottombarComponent: Controls,
63
65
  TopbarComponent: Navbar,
@@ -66,8 +68,8 @@ const VideoCallScreen = () => {
66
68
  SettingsComponent: SettingsView,
67
69
  VideocallAfterView: React.Fragment,
68
70
  VideocallBeforeView: React.Fragment,
69
- LeftbarComponent: React.Fragment,
70
- RightbarComponent: React.Fragment,
71
+ LeftbarComponent: Leftbar,
72
+ RightbarComponent: Rightbar,
71
73
  };
72
74
  if (
73
75
  data?.components?.videoCall &&
@@ -1,40 +1,44 @@
1
1
  import ParticipantsView from '../../components/ParticipantsView';
2
2
  import Chat from '../../components/Chat';
3
3
  import Navbar, {
4
- ParticipantsCountView,
5
- ParticipantsIconButton,
6
- ChatIconButton,
7
- SettingsIconButton,
4
+ MeetingTitleToolbarItem,
5
+ ParticipantCountToolbarItem,
6
+ RecordingStatusToolbarItem,
7
+ ChatToolbarItem,
8
+ ParticipantToolbarItem,
9
+ SettingsToobarItem,
8
10
  } from '../../components/Navbar';
9
- import CopyJoinInfo from '../../subComponents/CopyJoinInfo';
10
- import LayoutIconButton from '../../subComponents/LayoutIconButton';
11
- import SettingsView from '../../components/SettingsView';
12
- import Controls from '../../components/Controls';
13
- import LiveStreamControls from '../../components/livestream/views/LiveStreamControls';
14
- import LocalEndcall from '../../subComponents/LocalEndCall';
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';
20
-
11
+ import Controls, {
12
+ LayoutToolbarItem,
13
+ InviteToolbarItem,
14
+ RaiseHandToolbarItem,
15
+ LocalAudioToolbarItem,
16
+ LocalVideoToolbarItem,
17
+ SwitchCameraToolbarItem,
18
+ ScreenShareToolbarItem,
19
+ RecordingToolbarItem,
20
+ LocalEndcallToolbarItem,
21
+ } from '../../components/Controls';
21
22
  import ChatBubble from '../../subComponents/ChatBubble';
22
23
  import {ChatInput} from '../../subComponents/ChatInput';
24
+ import SettingsView from '../../components/SettingsView';
23
25
 
24
26
  const ToolbarComponents = {
25
- CopyJoinInfo,
26
- ParticipantsCountView,
27
- ParticipantsIconButton,
28
- ChatIconButton,
29
- LayoutIconButton,
30
- SettingsIconButton,
31
- LocalAudioMute,
32
- LocalVideoMute,
33
- LocalSwitchCamera,
34
- ScreenshareButton,
35
- Recording,
36
- LocalEndcall,
37
- LiveStreamControls,
27
+ MeetingTitleToolbarItem,
28
+ ParticipantCountToolbarItem,
29
+ RecordingStatusToolbarItem,
30
+ ChatToolbarItem,
31
+ ParticipantToolbarItem,
32
+ SettingsToobarItem,
33
+ LayoutToolbarItem,
34
+ InviteToolbarItem,
35
+ RaiseHandToolbarItem,
36
+ LocalAudioToolbarItem,
37
+ LocalVideoToolbarItem,
38
+ SwitchCameraToolbarItem,
39
+ ScreenShareToolbarItem,
40
+ RecordingToolbarItem,
41
+ LocalEndcallToolbarItem,
38
42
  };
39
43
  export {
40
44
  ParticipantsView,