agora-appbuilder-core 2.0.1 → 2.0.3-rc2

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.
Files changed (79) hide show
  1. package/Readme.md +2 -1
  2. package/package.json +1 -1
  3. package/template/_package-lock.json +8853 -8806
  4. package/template/agora-rn-uikit/.git/index +0 -0
  5. package/template/agora-rn-uikit/.git/logs/HEAD +2 -2
  6. package/template/agora-rn-uikit/.git/logs/refs/heads/ab-dev-auto +1 -1
  7. package/template/agora-rn-uikit/.git/logs/refs/heads/master +1 -1
  8. package/template/agora-rn-uikit/.git/logs/refs/remotes/origin/HEAD +1 -1
  9. package/template/agora-rn-uikit/.git/objects/pack/pack-2a0122bf5a3199f941e5a52535f43881623f752c.idx +0 -0
  10. package/template/agora-rn-uikit/.git/objects/pack/{pack-f379286d0537eb68377220b4929979324b8d5d1c.pack → pack-2a0122bf5a3199f941e5a52535f43881623f752c.pack} +0 -0
  11. package/template/agora-rn-uikit/.git/packed-refs +4 -2
  12. package/template/agora-rn-uikit/.git/refs/heads/ab-dev-auto +1 -1
  13. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +21 -0
  14. package/template/agora-rn-uikit/src/Controls/BtnTemplate.tsx +22 -14
  15. package/template/agora-rn-uikit/src/Controls/ImageIcon.tsx +17 -13
  16. package/template/agora-rn-uikit/src/Controls/types.ts +4 -0
  17. package/template/agora-rn-uikit/src/Rtc/Create.tsx +117 -19
  18. package/template/agora-rn-uikit/src/RtcConfigure.tsx +24 -13
  19. package/template/{src/utils/isSafariBrowser.tsx → agora-rn-uikit/src/Utils/isSafariBrowser.ts} +3 -0
  20. package/template/{src → agora-rn-uikit/src}/hooks/useImageDelay.tsx +5 -2
  21. package/template/agora-rn-uikit/src/index.ts +2 -0
  22. package/template/bridge/rtc/webNg/RtcEngine.ts +73 -70
  23. package/template/bridge/rtc/webNg/Types.ts +59 -5
  24. package/template/bridge/rtm/web/Types.ts +13 -0
  25. package/template/bridge/rtm/web/index.ts +78 -1
  26. package/template/global.d.ts +2 -0
  27. package/template/package-lock.json +8853 -8806
  28. package/template/package.json +3 -4
  29. package/template/react-native-toast-message/src/components/base/styles.js +4 -2
  30. package/template/src/assets/icons.ts +41 -28
  31. package/template/src/components/Chat.tsx +70 -184
  32. package/template/src/components/ChatContext.ts +13 -2
  33. package/template/src/components/Controls.native.tsx +37 -76
  34. package/template/src/components/Controls.tsx +50 -158
  35. package/template/src/components/DeviceConfigure.native.tsx +5 -1
  36. package/template/src/components/DeviceConfigure.tsx +38 -20
  37. package/template/src/components/Navbar.tsx +185 -226
  38. package/template/src/components/ParticipantsView.tsx +176 -184
  39. package/template/src/components/Precall.native.tsx +83 -69
  40. package/template/src/components/Precall.tsx +174 -191
  41. package/template/src/components/RTMConfigure.tsx +264 -78
  42. package/template/src/components/SettingsView.tsx +9 -19
  43. package/template/src/components/livestream/LiveStreamContext.tsx +411 -0
  44. package/template/src/components/livestream/Types.ts +69 -0
  45. package/template/src/components/livestream/index.ts +9 -0
  46. package/template/src/components/livestream/views/LiveStreamControls.tsx +27 -0
  47. package/template/src/components/participants/AllAudienceParticipants.tsx +53 -0
  48. package/template/src/components/participants/AllHostParticipants.tsx +65 -0
  49. package/template/src/components/participants/MeParticipant.tsx +37 -0
  50. package/template/src/components/participants/ParticipantName.tsx +47 -0
  51. package/template/src/components/participants/ParticipantSectionTitle.tsx +29 -0
  52. package/template/src/components/participants/RemoteParticipants.tsx +69 -0
  53. package/template/src/components/participants/ScreenshareParticipants.tsx +28 -0
  54. package/template/src/components/participants/context/ParticipantContext.tsx +97 -0
  55. package/template/src/components/styles.ts +13 -0
  56. package/template/src/pages/Create.tsx +25 -14
  57. package/template/src/pages/VideoCall.tsx +197 -159
  58. package/template/src/subComponents/ChatBubble.tsx +4 -1
  59. package/template/src/subComponents/ChatContainer.tsx +44 -20
  60. package/template/src/subComponents/ChatInput.tsx +4 -12
  61. package/template/src/subComponents/Checkbox.native.tsx +6 -5
  62. package/template/src/subComponents/Checkbox.tsx +1 -0
  63. package/template/src/subComponents/Recording.tsx +23 -9
  64. package/template/src/subComponents/RemoteVideoMute.tsx +2 -3
  65. package/template/src/subComponents/SelectDevice.tsx +70 -35
  66. package/template/src/subComponents/chat/ChatParticipants.tsx +121 -0
  67. package/template/src/subComponents/livestream/ApprovedLiveStreamControlsView.tsx +23 -0
  68. package/template/src/subComponents/livestream/CurrentLiveStreamRequestsView.tsx +70 -0
  69. package/template/src/subComponents/livestream/controls/LocalRaiseHand.tsx +57 -0
  70. package/template/src/subComponents/livestream/controls/RemoteLiveStreamApprovedRequestRecall.tsx +24 -0
  71. package/template/src/subComponents/livestream/controls/RemoteLiveStreamRequestApprove.tsx +38 -0
  72. package/template/src/subComponents/livestream/controls/RemoteLiveStreamRequestReject.tsx +37 -0
  73. package/template/src/subComponents/livestream/index.ts +18 -0
  74. package/template/src/subComponents/screenshare/ScreenshareButton.tsx +80 -0
  75. package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +24 -0
  76. package/template/src/subComponents/screenshare/ScreenshareConfigure.tsx +200 -0
  77. package/template/src/subComponents/screenshare/ScreenshareContext.tsx +21 -0
  78. package/template/src/utils/index.tsx +48 -0
  79. package/template/agora-rn-uikit/.git/objects/pack/pack-f379286d0537eb68377220b4929979324b8d5d1c.idx +0 -0
Binary file
@@ -1,2 +1,2 @@
1
- 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1643712825 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
2
- 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 3fad3e8996dbe300efbf023d89d38aae24bbb26f nitte93 <nitte.tiwari1993@gmail.com> 1643712825 +0530 checkout: moving from master to ab-dev-auto
1
+ 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1648625876 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
2
+ 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 bdb1c4d92e97621ce53b0f1120956832f7617af9 nitte93 <nitte.tiwari1993@gmail.com> 1648625876 +0530 checkout: moving from master to ab-dev-auto
@@ -1 +1 @@
1
- 0000000000000000000000000000000000000000 3fad3e8996dbe300efbf023d89d38aae24bbb26f nitte93 <nitte.tiwari1993@gmail.com> 1643712825 +0530 branch: Created from refs/remotes/origin/ab-dev-auto
1
+ 0000000000000000000000000000000000000000 bdb1c4d92e97621ce53b0f1120956832f7617af9 nitte93 <nitte.tiwari1993@gmail.com> 1648625876 +0530 branch: Created from refs/remotes/origin/ab-dev-auto
@@ -1 +1 @@
1
- 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1643712825 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
1
+ 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1648625876 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
@@ -1 +1 @@
1
- 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1643712825 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
1
+ 0000000000000000000000000000000000000000 7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 nitte93 <nitte.tiwari1993@gmail.com> 1648625876 +0530 clone: from https://github.com/AgoraIO-Community/ReactNative-UIKit
@@ -1,12 +1,14 @@
1
1
  # pack-refs with: peeled fully-peeled sorted
2
- 3fad3e8996dbe300efbf023d89d38aae24bbb26f refs/remotes/origin/ab-dev-auto
2
+ bdb1c4d92e97621ce53b0f1120956832f7617af9 refs/remotes/origin/ab-dev-auto
3
3
  fd59daf4876e4eb142856957f9450ef5175f4e0f refs/remotes/origin/app-builder
4
4
  c60cde905c722aeaf8f25aeed2910a7b4d532cfa refs/remotes/origin/app-builder-dev
5
5
  83064dc5031b9a88a170c6b91ee725ad072eadb1 refs/remotes/origin/bugfix/camera-green-light-on-precall
6
6
  5d4f38ae0a89b20275e2497a4e8f43b0ca2830e4 refs/remotes/origin/dev
7
7
  4ec093e51924049e37d247d867a5482a9376a8cf refs/remotes/origin/docs
8
8
  002960cf34f06ffd3e905e6de9865460f02bd910 refs/remotes/origin/feat/icon-tint
9
- 233d494c4dfc2a18ce598f3a12f419ec894e91f5 refs/remotes/origin/feature/ui-kit/raise-hand
9
+ c7d21d289c4e930f57cbe3a2eefbbfc0d9741c6f refs/remotes/origin/feature/fpe-internationlization
10
+ bfdb92d89cf66d270bed81748c39330846532d07 refs/remotes/origin/feature/life_cycle_events
11
+ 7c60e844f617d9c0faf6aa968fdf16b16d0e59ce refs/remotes/origin/feature/ui-kit/raise-hand
10
12
  7f9cd41e1bf3ea1b110e2706c7e4c5a921230647 refs/remotes/origin/master
11
13
  01ad019ae514b6502b64a30b357a20af49b8c8a5 refs/remotes/origin/refactor
12
14
  ac8929fda04db3663a7c1eb9c71ca6a97728f0eb refs/remotes/origin/v2-legacy
@@ -1 +1 @@
1
- 3fad3e8996dbe300efbf023d89d38aae24bbb26f
1
+ bdb1c4d92e97621ce53b0f1120956832f7617af9
@@ -4,6 +4,24 @@ import {RtcEngineEvents} from 'react-native-agora/lib/typescript/src/common/RtcE
4
4
  import {EncryptionMode} from 'react-native-agora';
5
5
  import {VideoProfile} from '../Utils/quality';
6
6
 
7
+ /* User role for live streaming mode */
8
+ export enum ClientRole {
9
+ /* 1: A host can both send and receive streams. */
10
+ Broadcaster = 1,
11
+ /* 2: The default role. An audience can only receive streams. */
12
+ Audience = 2,
13
+ }
14
+
15
+ /* Mode for RTC (Live or Broadcast) */
16
+ export enum ChannelProfile {
17
+ /** 0: (Default) The Communication profile.
18
+ * Use this profile in one-on-one calls or group calls, where all users can talk freely. */
19
+ Communication = 0,
20
+ /** 1: The Live-Broadcast profile.
21
+ * Users in a live-broadcast channel have a role as either host or audience. A host can both send and receive streams; an audience can only receive streams. */
22
+ LiveBroadcasting = 1,
23
+ }
24
+
7
25
  // disabled is intentionally kept as the 1st item in the enum.
8
26
  // This way, it evaluates to a falsy value in a if statement
9
27
  export enum ToggleState {
@@ -29,6 +47,7 @@ interface remoteBtnStylesInterface {
29
47
  muteRemoteVideo?: StyleProp<ViewStyle>;
30
48
  remoteSwap?: StyleProp<ViewStyle>;
31
49
  minCloseBtnStyles?: StyleProp<ViewStyle>;
50
+ liveStreamHostControlBtns?: StyleProp<ViewStyle>;
32
51
  }
33
52
 
34
53
  interface localBtnStylesInterface {
@@ -64,6 +83,7 @@ export interface RtcPropsInterface {
64
83
  dual?: boolean | null;
65
84
  profile?: VideoProfile;
66
85
  initialDualStreamMode?: DualStreamMode;
86
+ role?: ClientRole /* Set local user's role between audience and host. Use with mode set to livestreaming. (default: host) */;
67
87
  callActive?: boolean;
68
88
  encryption?: {
69
89
  key: string;
@@ -97,6 +117,7 @@ export interface PropsInterface {
97
117
  rtcProps: RtcPropsInterface;
98
118
  styleProps?: Partial<StylePropInterface>;
99
119
  callbacks?: Partial<CallbacksInterface>;
120
+ mode?: ChannelProfile;
100
121
  }
101
122
 
102
123
  const initialValue: PropsInterface = {
@@ -7,21 +7,31 @@ import {
7
7
  ViewStyle,
8
8
  Text,
9
9
  View,
10
+ Platform,
10
11
  } from 'react-native';
11
12
  import PropsContext from '../Contexts/PropsContext';
12
13
  import styles from '../Style';
13
14
  import icons, {IconsInterface} from './Icons';
14
- import useImageDelay from '../../../src/hooks/useImageDelay';
15
- import isSafariBrowser from '../../../src/utils/isSafariBrowser';
15
+ import useImageDelay from '../hooks/useImageDelay';
16
+ import {Either} from './types';
16
17
 
17
- interface BtnTemplateInterface {
18
- name: keyof IconsInterface;
18
+ interface BtnTemplateBasicInterface {
19
19
  color?: string;
20
20
  onPress?: TouchableOpacityProps['onPress'];
21
21
  style?: StyleProp<ViewStyle>;
22
22
  btnText?: string;
23
23
  disabled?: boolean;
24
24
  }
25
+ interface BtnTemplateInterfaceWithName extends BtnTemplateBasicInterface {
26
+ name?: keyof IconsInterface;
27
+ }
28
+ interface BtnTemplateInterfaceWithIcon extends BtnTemplateBasicInterface {
29
+ icon?: any;
30
+ }
31
+ type BtnTemplateInterface = Either<
32
+ BtnTemplateInterfaceWithIcon,
33
+ BtnTemplateInterfaceWithName
34
+ >;
25
35
 
26
36
  const BtnTemplate: React.FC<BtnTemplateInterface> = (props) => {
27
37
  const {disabled = false} = props;
@@ -30,10 +40,8 @@ const BtnTemplate: React.FC<BtnTemplateInterface> = (props) => {
30
40
 
31
41
  const imageRef = React.useRef(null);
32
42
 
33
- if (isSafariBrowser()) {
34
- // This fixes the tint issue in safari browser
35
- useImageDelay(imageRef, 10, '');
36
- }
43
+ // This fixes the tint issue in safari browser
44
+ useImageDelay(imageRef, 10, '', props?.color || '');
37
45
 
38
46
  return (
39
47
  <TouchableOpacity
@@ -46,23 +54,23 @@ const BtnTemplate: React.FC<BtnTemplateInterface> = (props) => {
46
54
  props.style as object,
47
55
  ]}>
48
56
  <Image
49
- ref={imageRef}
57
+ ref={Platform.OS === 'web' ? imageRef : undefined}
50
58
  style={{
51
59
  width: '100%',
52
60
  height: '100%',
53
- opacity: disabled ? 0.4 : 1,
54
- tintColor: disabled ? 'grey' : ( props.color || theme || '#fff')
61
+ opacity: disabled ? 0.4 : 1,
62
+ tintColor: disabled ? 'grey' : props.color || theme || '#fff',
55
63
  }}
56
64
  resizeMode={'contain'}
57
- source={{uri: icons[props.name]}}
65
+ source={{uri: props.name ? icons[props.name] : props.icon}}
58
66
  />
59
67
  </View>
60
68
  <Text
61
69
  style={{
62
70
  textAlign: 'center',
63
71
  marginTop: 5,
64
- color: disabled ? 'grey' : (props.color || theme || '#fff'),
65
- opacity: disabled ? 0.4 : 1
72
+ color: disabled ? 'grey' : props.color || theme || '#fff',
73
+ opacity: disabled ? 0.4 : 1,
66
74
  }}>
67
75
  {props.btnText}
68
76
  </Text>
@@ -10,42 +10,46 @@
10
10
  *********************************************
11
11
  */
12
12
  import React, {useContext} from 'react';
13
- import {Image, StyleProp, ViewStyle} from 'react-native';
13
+ import {Image, Platform, StyleProp, ViewStyle} from 'react-native';
14
14
  import icons, {IconsInterface} from './Icons';
15
15
  import PropsContext from './../Contexts/PropsContext';
16
- import useImageDelay from './../../../src/hooks/useImageDelay';
17
- import isSafariBrowser from '../../../src/utils/isSafariBrowser';
16
+ import useImageDelay from '../hooks/useImageDelay';
17
+ import {Either} from './types';
18
18
 
19
- interface ImageIconInterface {
20
- name: keyof IconsInterface;
19
+ interface BaseInterface {
21
20
  color?: string;
22
21
  style?: StyleProp<ViewStyle>;
23
22
  }
24
23
 
24
+ interface BaseInterfaceWithName extends BaseInterface {
25
+ name?: keyof IconsInterface;
26
+ }
27
+ interface BaseInterfaceWithIcon extends BaseInterface {
28
+ icon?: any;
29
+ }
30
+
31
+ type ImageIconInterface = Either<BaseInterfaceWithName, BaseInterfaceWithIcon>;
32
+
25
33
  const ImageIcon: React.FC<ImageIconInterface> = (props) => {
26
34
  const {styleProps} = useContext(PropsContext);
27
35
  const {theme} = styleProps || {};
28
36
  const imageRef = React.useRef(null);
29
37
 
30
- if (isSafariBrowser()) {
31
- // This hook renders the image after a delay to fix
32
- // tint issue in safari browser
33
- useImageDelay(imageRef, 10, props.name);
34
- }
38
+ useImageDelay(imageRef, 10, props?.name || '', props?.color);
35
39
 
36
40
  return (
37
41
  <Image
38
- ref={imageRef}
42
+ ref={Platform.OS === 'web' ? imageRef : undefined}
39
43
  style={[
40
44
  {
41
45
  width: '100%',
42
46
  height: '100%',
43
- tintColor: props.color || theme || '#fff'
47
+ tintColor: props.color || theme || '#fff',
44
48
  },
45
49
  props.style as object,
46
50
  ]}
47
51
  resizeMode={'contain'}
48
- source={{uri: icons[props.name]}}
52
+ source={{uri: props.name ? icons[props.name] : props.icon}}
49
53
  />
50
54
  );
51
55
  };
@@ -0,0 +1,4 @@
1
+ export type Only<T, U> = {[P in keyof T]: T[P]} &
2
+ Omit<{[P in keyof U]?: never}, keyof T>;
3
+
4
+ export type Either<T, U> = Only<T, U> | Only<U, T>;
@@ -6,7 +6,11 @@ import RtcEngine, {
6
6
  import {Platform} from 'react-native';
7
7
  import requestCameraAndAudioPermission from '../Utils/permission';
8
8
  import {DispatchType} from '../Contexts/RtcContext';
9
- import PropsContext, {ToggleState} from '../Contexts/PropsContext';
9
+ import PropsContext, {
10
+ ToggleState,
11
+ ClientRole,
12
+ ChannelProfile,
13
+ } from '../Contexts/PropsContext';
10
14
  import quality from '../Utils/quality';
11
15
 
12
16
  const Create = ({
@@ -17,8 +21,10 @@ const Create = ({
17
21
  children: (engine: React.MutableRefObject<RtcEngine>) => JSX.Element;
18
22
  }) => {
19
23
  const [ready, setReady] = useState(false);
20
- const {callbacks, rtcProps} = useContext(PropsContext);
24
+ const {callbacks, rtcProps, mode} = useContext(PropsContext);
21
25
  let engine = useRef<RtcEngine>({} as RtcEngine);
26
+ const isVideoEnabledRef = useRef<boolean>(false);
27
+ const firstUpdate = useRef(true);
22
28
 
23
29
  useEffect(() => {
24
30
  async function init() {
@@ -36,6 +42,19 @@ const Create = ({
36
42
  } else {
37
43
  engine.current = await RtcEngine.create(rtcProps.appId);
38
44
  }
45
+ /* Live Streaming */
46
+ if (mode == ChannelProfile.LiveBroadcasting) {
47
+ await engine.current.setChannelProfile(
48
+ ChannelProfile.LiveBroadcasting,
49
+ );
50
+ await engine.current.setClientRole(
51
+ rtcProps.role === ClientRole.Audience
52
+ ? ClientRole.Audience
53
+ : ClientRole.Broadcaster,
54
+ );
55
+ } else {
56
+ await engine.current.setChannelProfile(ChannelProfile.Communication);
57
+ }
39
58
  if (rtcProps.profile) {
40
59
  if (Platform.OS === 'web') {
41
60
  // move this to bridge?
@@ -49,18 +68,36 @@ const Create = ({
49
68
  });
50
69
  }
51
70
  }
52
- try {
53
- await engine.current.enableVideo();
54
- } catch (e) {
55
- dispatch({
56
- type: 'LocalMuteAudio',
57
- value: [ToggleState.disabled],
58
- });
59
- dispatch({
60
- type: 'LocalMuteVideo',
61
- value: [ToggleState.disabled],
62
- });
63
- console.error('No devices', e);
71
+
72
+ /**
73
+ * API enableVideo :
74
+ * On Web -> Asks permissions and then creates microphone and camera tracks
75
+ * On Native -> Used to start the call in video module
76
+ * The following condition allows enableVideo API to run in all the conditions
77
+ * except when mode is livestreaming && user is attendee && running on web,
78
+ * thereby not asking permissions and not creating tracks for attendees
79
+ */
80
+ if (
81
+ !(
82
+ mode === ChannelProfile.LiveBroadcasting &&
83
+ rtcProps.role == ClientRole.Audience &&
84
+ Platform.OS === 'web'
85
+ )
86
+ ) {
87
+ try {
88
+ await engine.current.enableVideo();
89
+ isVideoEnabledRef.current = true;
90
+ } catch (e) {
91
+ dispatch({
92
+ type: 'LocalMuteAudio',
93
+ value: [ToggleState.disabled],
94
+ });
95
+ dispatch({
96
+ type: 'LocalMuteVideo',
97
+ value: [ToggleState.disabled],
98
+ });
99
+ console.error('No devices', e);
100
+ }
64
101
  }
65
102
 
66
103
  engine.current.addListener(
@@ -95,8 +132,6 @@ const Create = ({
95
132
  });
96
133
 
97
134
  engine.current.addListener('RemoteAudioStateChanged', (...args) => {
98
- // console.log('RemoteAudioStateChanged', args);
99
-
100
135
  dispatch({
101
136
  type: 'RemoteAudioStateChanged',
102
137
  value: args,
@@ -108,8 +143,6 @@ const Create = ({
108
143
  });
109
144
 
110
145
  engine.current.addListener('RemoteVideoStateChanged', (...args) => {
111
- // console.log('RemoteVideoStateChanged', args);
112
-
113
146
  dispatch({
114
147
  type: 'RemoteVideoStateChanged',
115
148
  value: args,
@@ -124,7 +157,72 @@ const Create = ({
124
157
  return () => {
125
158
  engine.current!.destroy();
126
159
  };
127
- }, []);
160
+ }, [rtcProps.appId]);
161
+
162
+ useEffect(() => {
163
+ const toggleRole = async () => {
164
+ if (mode == ChannelProfile.LiveBroadcasting) {
165
+ if (rtcProps.role == ClientRole.Broadcaster) {
166
+ await engine.current?.setClientRole(ClientRole.Broadcaster);
167
+ // isVideoEnabledRef checks if the permission is already taken once
168
+ if (!isVideoEnabledRef.current) {
169
+ try {
170
+ // This creates local audio and video track
171
+ await engine.current?.enableVideo();
172
+ isVideoEnabledRef.current = true;
173
+ } catch (error) {
174
+ dispatch({
175
+ type: 'LocalMuteAudio',
176
+ value: [ToggleState.disabled],
177
+ });
178
+ dispatch({
179
+ type: 'LocalMuteVideo',
180
+ value: [ToggleState.disabled],
181
+ });
182
+ }
183
+ }
184
+ if (isVideoEnabledRef.current) {
185
+ // This unpublishes the current track
186
+ await engine.current?.muteLocalAudioStream(true);
187
+ await engine.current?.muteLocalVideoStream(true);
188
+ // This updates the uid interface
189
+ dispatch({
190
+ type: 'LocalMuteAudio',
191
+ value: [ToggleState.disabled],
192
+ });
193
+ dispatch({
194
+ type: 'LocalMuteVideo',
195
+ value: [ToggleState.disabled],
196
+ });
197
+ }
198
+ }
199
+ if (rtcProps.role == ClientRole.Audience) {
200
+ /**
201
+ * To switch the user role back to "audience", call unpublish first
202
+ * Otherwise the setClientRole method call fails and throws an exception.
203
+ */
204
+ await engine.current?.muteLocalAudioStream(true);
205
+ await engine.current?.muteLocalVideoStream(true);
206
+ dispatch({
207
+ type: 'LocalMuteAudio',
208
+ value: [ToggleState.disabled],
209
+ });
210
+ dispatch({
211
+ type: 'LocalMuteVideo',
212
+ value: [ToggleState.disabled],
213
+ });
214
+ await engine.current?.setClientRole(ClientRole.Audience);
215
+ }
216
+ }
217
+ };
218
+ // The firstUpdateCurrent ref skips the render of this block for the first time
219
+ if (firstUpdate.current) {
220
+ firstUpdate.current = false;
221
+ return;
222
+ }
223
+ toggleRole();
224
+ }, [rtcProps.role]);
225
+
128
226
  return (
129
227
  <>
130
228
  {
@@ -11,6 +11,8 @@ import PropsContext, {
11
11
  RtcPropsInterface,
12
12
  CallbacksInterface,
13
13
  DualStreamMode,
14
+ ClientRole,
15
+ ChannelProfile,
14
16
  } from './Contexts/PropsContext';
15
17
  import {MinUidProvider} from './Contexts/MinUidContext';
16
18
  import {MaxUidProvider} from './Contexts/MaxUidContext';
@@ -30,23 +32,32 @@ import {
30
32
  import Create from './Rtc/Create';
31
33
  import Join from './Rtc/Join';
32
34
 
33
- const initialLocalState: UidStateInterface = {
34
- min: [],
35
- max: [
36
- {
37
- uid: 'local',
38
- audio: ToggleState.enabled,
39
- video: ToggleState.enabled,
40
- streamType: 'high',
41
- },
42
- ],
43
- };
44
-
45
35
  const RtcConfigure: React.FC<Partial<RtcPropsInterface>> = (props) => {
46
- const {callbacks, rtcProps} = useContext(PropsContext);
36
+ const {callbacks, rtcProps, mode} = useContext(PropsContext);
47
37
  let [dualStreamMode, setDualStreamMode] = useState<DualStreamMode>(
48
38
  rtcProps?.initialDualStreamMode || DualStreamMode.DYNAMIC,
49
39
  );
40
+
41
+ const initialLocalState: UidStateInterface = {
42
+ min: [],
43
+ max: [
44
+ {
45
+ uid: 'local',
46
+ audio:
47
+ mode == ChannelProfile.LiveBroadcasting &&
48
+ rtcProps?.role == ClientRole.Audience
49
+ ? ToggleState.disabled
50
+ : ToggleState.enabled,
51
+ video:
52
+ mode == ChannelProfile.LiveBroadcasting &&
53
+ rtcProps?.role == ClientRole.Audience
54
+ ? ToggleState.disabled
55
+ : ToggleState.enabled,
56
+ streamType: 'high',
57
+ },
58
+ ],
59
+ };
60
+
50
61
  const [initialState, setInitialState] = React.useState(
51
62
  JSON.parse(JSON.stringify(initialLocalState)),
52
63
  );
@@ -1,4 +1,7 @@
1
+ import {Platform} from 'react-native';
2
+
1
3
  const isSafariBrowser = () => {
4
+ if (Platform.OS !== 'web') return false;
2
5
  if (!('userAgent' in navigator)) {
3
6
  console.warn('unable to detect browser');
4
7
  return false;
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
+ import isSafariBrowser from '../Utils/isSafariBrowser';
2
3
 
3
4
  function useImageDelay(
4
5
  elementRef: any,
5
6
  delay: number | null,
6
7
  imageName: string,
8
+ tintColor?: string,
7
9
  ) {
8
- // Remember the latest callback if it changes.
9
10
  React.useEffect(() => {
11
+ if (!isSafariBrowser()) return;
10
12
  // The following block allows to repaint the icon
11
13
  let imageElement: any;
12
14
  if (elementRef && elementRef.current) {
@@ -21,8 +23,9 @@ function useImageDelay(
21
23
  imageElement.style.display = 'initial';
22
24
  }
23
25
  }, delay);
26
+
24
27
  return () => clearTimeout(timer);
25
- }, [imageName]);
28
+ }, [imageName, tintColor]);
26
29
  }
27
30
 
28
31
  export default useImageDelay;
@@ -38,6 +38,8 @@ export type {
38
38
  PropsInterface,
39
39
  } from './Contexts/PropsContext';
40
40
 
41
+ export {ClientRole, ChannelProfile} from './Contexts/PropsContext';
42
+
41
43
  export {
42
44
  default as RtcContext,
43
45
  RtcConsumer,