agora-appbuilder-core 4.0.0-beta.5 → 4.0.0-beta.6

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 (33) hide show
  1. package/package.json +1 -1
  2. package/template/global.d.ts +2 -2
  3. package/template/src/AppWrapper.tsx +1 -3
  4. package/template/src/atoms/Dropdown.tsx +1 -1
  5. package/template/src/atoms/PrimaryButton.tsx +1 -1
  6. package/template/src/atoms/Toggle.tsx +1 -1
  7. package/template/src/components/Controls.tsx +1 -0
  8. package/template/src/components/DeviceConfigure.tsx +48 -7
  9. package/template/src/components/NetworkQualityContext.tsx +3 -3
  10. package/template/src/components/PinnedVideo.tsx +9 -1
  11. package/template/src/components/Precall.native.tsx +1 -1
  12. package/template/src/components/Precall.tsx +1 -1
  13. package/template/src/components/SettingsView.tsx +1 -1
  14. package/template/src/components/participants/Participant.tsx +2 -2
  15. package/template/src/pages/Authenticate.tsx +1 -1
  16. package/template/src/pages/Create.tsx +1 -1
  17. package/template/src/pages/video-call/ActionSheet.native.tsx +1 -1
  18. package/template/src/pages/video-call/ActionSheet.tsx +1 -1
  19. package/template/src/pages/video-call/ActionSheetContent.tsx +2 -2
  20. package/template/src/pages/video-call/ActionSheetHandle.tsx +1 -1
  21. package/template/src/pages/video-call/VideoCallMobileView.tsx +41 -39
  22. package/template/src/pages/video-call/VideoRenderer.tsx +11 -4
  23. package/template/src/subComponents/ChatInput.ios.tsx +1 -1
  24. package/template/src/subComponents/ChatInput.tsx +1 -1
  25. package/template/src/subComponents/Illustration.tsx +2 -2
  26. package/template/src/subComponents/LocalAudioMute.tsx +3 -3
  27. package/template/src/subComponents/LocalSwitchCamera.tsx +1 -1
  28. package/template/src/subComponents/LocalVideoMute.tsx +3 -3
  29. package/template/src/subComponents/RemoteAudioMute.tsx +1 -1
  30. package/template/src/subComponents/RemoteVideoMute.tsx +1 -1
  31. package/template/src/subComponents/chat/ChatParticipants.tsx +1 -1
  32. package/template/src/subComponents/screenshare/ScreenshareButton.tsx +1 -1
  33. package/template/src/utils/useMuteToggleLocal.ts +45 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.0-beta.5",
3
+ "version": "4.0.0-beta.6",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -76,7 +76,7 @@ interface ConfigInterface {
76
76
  PRIMARY_ACTION_TEXT_COLOR: string;
77
77
  SECONDARY_ACTION_COLOR: string;
78
78
  FONT_COLOR: string;
79
- BACKGROUND_IMAGE: string;
79
+ BG: string;
80
80
  BACKGROUND_COLOR: string;
81
81
  VIDEO_AUDIO_TILE_COLOR: string;
82
82
  VIDEO_AUDIO_TILE_OVERLAY_COLOR: string;
@@ -85,7 +85,7 @@ interface ConfigInterface {
85
85
  SEMANTIC_ERROR: string;
86
86
  SEMANTIC_SUCCESS: string;
87
87
  SEMANTIC_WARNING: string;
88
- SEMANTIC_NETRUAL: string;
88
+ SEMANTIC_NEUTRAL: string;
89
89
  INPUT_FIELD_BACKGROUND_COLOR: string;
90
90
  INPUT_FIELD_BORDER_COLOR: string;
91
91
  CARD_LAYER_1_COLOR: string;
@@ -75,9 +75,7 @@ const AppWrapper = (props: AppWrapperProps) => {
75
75
 
76
76
  return (
77
77
  <AppRoot>
78
- <ImageBackgroundComp
79
- bg={$config.BACKGROUND_IMAGE}
80
- color={$config.BACKGROUND_COLOR}>
78
+ <ImageBackgroundComp bg={$config.BG} color={$config.BACKGROUND_COLOR}>
81
79
  <SafeAreaView
82
80
  // @ts-ignore textAlign not supported by TS definitions but is applied to web regardless
83
81
  style={[{flex: 1}, Platform.select({web: {textAlign: 'left'}})]}>
@@ -169,7 +169,7 @@ const Dropdown: FC<Props> = ({
169
169
  iconType="plain"
170
170
  name={icon}
171
171
  iconSize={20}
172
- tintColor={$config.SEMANTIC_NETRUAL}
172
+ tintColor={$config.SEMANTIC_NEUTRAL}
173
173
  />
174
174
  </View>
175
175
  ) : (
@@ -38,7 +38,7 @@ export default function PrimaryButton(props: PrimaryButtonProps) {
38
38
  style={[
39
39
  styles.container,
40
40
  props?.disabled
41
- ? {backgroundColor: $config.SEMANTIC_NETRUAL}
41
+ ? {backgroundColor: $config.SEMANTIC_NEUTRAL}
42
42
  : {backgroundColor: $config.PRIMARY_ACTION_BRAND_COLOR},
43
43
  containerStyle ? containerStyle : {},
44
44
  ]}
@@ -20,7 +20,7 @@ const Toggle = (props: SwitchProps) => {
20
20
  onValueChange={toggleSwitch}
21
21
  disabled={false}
22
22
  backgroundActive={$config.PRIMARY_ACTION_BRAND_COLOR}
23
- backgroundInactive={$config.SEMANTIC_NETRUAL}
23
+ backgroundInactive={$config.SEMANTIC_NEUTRAL}
24
24
  circleActiveColor={$config.CARD_LAYER_1_COLOR}
25
25
  circleInActiveColor={$config.CARD_LAYER_1_COLOR}
26
26
  // renderInsideCircle={() => <CustomComponent />} // custom component to render inside the Switch circle (Text, Image, etc.)
@@ -423,6 +423,7 @@ const style = StyleSheet.create({
423
423
  alignItems: 'center',
424
424
  },
425
425
  centerContent: {
426
+ zIndex: 2,
426
427
  flex: 1,
427
428
  flexDirection: 'row',
428
429
  justifyContent: 'center',
@@ -224,6 +224,15 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
224
224
  }
225
225
  };
226
226
 
227
+ useEffect(() => {
228
+ const interval = setInterval(() => {
229
+ navigator.mediaDevices.enumerateDevices();
230
+ }, 2000);
231
+ return () => {
232
+ clearInterval(interval);
233
+ };
234
+ }, []);
235
+
227
236
  useEffect(() => {
228
237
  // Labels are empty in firefox when permission is granted first time
229
238
  // refresh device list if labels are empty
@@ -307,6 +316,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
307
316
  const commonOnChangedEvent = async (changedDeviceData: DeviceInfo) => {
308
317
  // Extracted devicelist because we want to perform fallback with
309
318
  // the most current version.
319
+ const previousDeviceList = deviceList;
310
320
  const updatedDeviceList = await refreshDeviceList();
311
321
  const changedDevice = changedDeviceData.device;
312
322
 
@@ -330,7 +340,40 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
330
340
 
331
341
  log(logTag, changedDeviceData);
332
342
 
333
- if (changedDeviceData.state === 'ACTIVE') {
343
+ if (currentDevice === 'default') {
344
+ // const previousDefaultDevice = previousDeviceList.find(
345
+ // (device) => device.deviceId === 'default',
346
+ // );
347
+ // const currentDefaultDevice = updatedDeviceList.find(
348
+ // (device) => device.deviceId === 'default',
349
+ // );
350
+ // log(logTag, 'current Default device', {
351
+ // changedDeviceData,
352
+ // previousDeviceList,
353
+ // updatedDeviceList,
354
+ // previousDefaultDevice,
355
+ // currentDefaultDevice,
356
+ // });
357
+ // if (previousDefaultDevice.groupId !== currentDefaultDevice.groupId) {
358
+ // log(logTag, 'Default device changed', {
359
+ // changedDeviceData,
360
+ // previousDeviceList,
361
+ // updatedDeviceList,
362
+ // previousDefaultDevice,
363
+ // currentDefaultDevice,
364
+ // });
365
+ // setCurrentDevice('default');
366
+ // }
367
+ setCurrentDevice('default');
368
+ }
369
+
370
+ const didChangeDeviceExistBefore = previousDeviceList.find(
371
+ (device) => device.deviceId === changedDevice.deviceId,
372
+ )
373
+ ? true
374
+ : false;
375
+
376
+ if (changedDeviceData.state === 'ACTIVE' && !didChangeDeviceExistBefore) {
334
377
  const rememberedDevice =
335
378
  rememberedDevicesList[changedDevice.kind][changedDevice.deviceId];
336
379
 
@@ -348,24 +391,22 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
348
391
  return;
349
392
  }
350
393
  }
351
- if (selectedMic === 'default') {
352
- setCurrentDevice('default');
353
- }
354
394
  };
355
395
 
356
396
  // Port this to useEffectEvent(https://beta.reactjs.org/reference/react/useEffectEvent) when
357
397
  // released
358
398
  useEffect(() => {
399
+ log('previous devicelist updated', deviceList);
359
400
  AgoraRTC.onMicrophoneChanged = commonOnChangedEvent;
360
- }, [selectedMic]);
401
+ }, [selectedMic, deviceList]);
361
402
 
362
403
  useEffect(() => {
363
404
  AgoraRTC.onPlaybackDeviceChanged = commonOnChangedEvent;
364
- }, [selectedSpeaker]);
405
+ }, [selectedSpeaker, deviceList]);
365
406
 
366
407
  useEffect(() => {
367
408
  AgoraRTC.onCameraChanged = commonOnChangedEvent;
368
- }, [selectedCam]);
409
+ }, [selectedCam, deviceList]);
369
410
 
370
411
  const setSelectedMic = (deviceId: deviceId) => {
371
412
  log('mic: setting to', deviceId);
@@ -37,7 +37,7 @@ export const networkIconsObject: {
37
37
  } = {
38
38
  0: {
39
39
  icon: 'connection-unsupported',
40
- tint: $config.SEMANTIC_NETRUAL,
40
+ tint: $config.SEMANTIC_NEUTRAL,
41
41
  text: 'unknown',
42
42
  },
43
43
  1: {
@@ -72,12 +72,12 @@ export const networkIconsObject: {
72
72
  },
73
73
  7: {
74
74
  icon: 'connection-unpublished',
75
- tint: $config.SEMANTIC_NETRUAL,
75
+ tint: $config.SEMANTIC_NEUTRAL,
76
76
  text: 'unpublished',
77
77
  },
78
78
  8: {
79
79
  icon: 'connection-loading',
80
- tint: $config.SEMANTIC_NETRUAL,
80
+ tint: $config.SEMANTIC_NEUTRAL,
81
81
  text: 'loading',
82
82
  },
83
83
  };
@@ -127,10 +127,18 @@ const PinnedVideo: layoutComponent = ({renderData}) => {
127
127
  }
128
128
  return (
129
129
  <Pressable
130
+ //old
130
131
  //if user pinned somebody then side panel items should not be clickable - swap video should be called
131
132
  //instead we will show replace pin button on hovering the video tile
133
+ //old
132
134
  disabled={
133
- activeSpeaker || pinnedUid || screenShareOn ? true : false
135
+ //old fix
136
+ //activeSpeaker || pinnedUid || screenShareOn ? true : false
137
+ //old fix
138
+
139
+ //latest fix : pinned video sidepanel layout should not be clickable
140
+ //if user hover on it we will show pin for me/replace pin(if someone already pinned) button
141
+ true
134
142
  }
135
143
  style={
136
144
  isSidePinnedlayout
@@ -379,7 +379,7 @@ const style = StyleSheet.create({
379
379
  fontWeight: '400',
380
380
  fontSize: ThemeConfig.FontSize.small,
381
381
  lineHeight: 18,
382
- color: $config.SEMANTIC_NETRUAL,
382
+ color: $config.SEMANTIC_NEUTRAL,
383
383
  textAlign: 'left',
384
384
  },
385
385
  btnContainerStyle: {maxWidth: 337, alignSelf: 'center', marginTop: 50},
@@ -382,7 +382,7 @@ const style = StyleSheet.create({
382
382
  fontWeight: '400',
383
383
  fontSize: ThemeConfig.FontSize.small,
384
384
  lineHeight: 18,
385
- color: $config.SEMANTIC_NETRUAL,
385
+ color: $config.SEMANTIC_NEUTRAL,
386
386
  textAlign: 'left',
387
387
  },
388
388
  btnContainerStyle: {maxWidth: 337, alignSelf: 'center', marginTop: 50},
@@ -116,7 +116,7 @@ const EditName: React.FC = (props?: EditNameProps) => {
116
116
  name="person"
117
117
  iconSize={20}
118
118
  iconType="plain"
119
- tintColor={$config.SEMANTIC_NETRUAL}
119
+ tintColor={$config.SEMANTIC_NEUTRAL}
120
120
  />
121
121
  <TextInput
122
122
  maxLength={maxInputLimit}
@@ -169,7 +169,7 @@ const Participant = (props: ParticipantInterface) => {
169
169
  tintColor: isVideoEnabled
170
170
  ? $config.PRIMARY_ACTION_BRAND_COLOR
171
171
  : isPermissionDenied
172
- ? $config.SEMANTIC_NETRUAL
172
+ ? $config.SEMANTIC_NEUTRAL
173
173
  : $config.SEMANTIC_ERROR,
174
174
  };
175
175
  }}
@@ -202,7 +202,7 @@ const Participant = (props: ParticipantInterface) => {
202
202
  tintColor: isAudioEnabled
203
203
  ? $config.PRIMARY_ACTION_BRAND_COLOR
204
204
  : isPermissionDenied
205
- ? $config.SEMANTIC_NETRUAL
205
+ ? $config.SEMANTIC_NEUTRAL
206
206
  : $config.SEMANTIC_ERROR,
207
207
  };
208
208
  }}
@@ -33,7 +33,7 @@ const Authenticate = () => {
33
33
  const oauthProviderLabel = 'Please select an OAuth provider to login.';
34
34
  return (
35
35
  <ImageBackground
36
- source={{uri: $config.BACKGROUND_IMAGE}}
36
+ source={{uri: $config.BG}}
37
37
  style={style.full}
38
38
  resizeMode={'cover'}>
39
39
  <View style={style.main}>
@@ -181,7 +181,7 @@ const Create = () => {
181
181
  tintColor={
182
182
  isToolTipVisible
183
183
  ? $config.SECONDARY_ACTION_COLOR
184
- : $config.SEMANTIC_NETRUAL
184
+ : $config.SEMANTIC_NEUTRAL
185
185
  }
186
186
  />
187
187
  </Pressable>
@@ -199,7 +199,7 @@ const styles = StyleSheet.create({
199
199
  },
200
200
 
201
201
  handleIndicatorStyle: {
202
- // backgroundColor: $config.SEMANTIC_NETRUAL,
202
+ // backgroundColor: $config.SEMANTIC_NEUTRAL,
203
203
  // width: 40,
204
204
  // height: 4,
205
205
  },
@@ -43,7 +43,7 @@ const ActionSheet = () => {
43
43
 
44
44
  useEffect(() => {
45
45
  root.style.setProperty('--sheet-background', $config.CARD_LAYER_1_COLOR);
46
- root.style.setProperty('--handle-background', $config.SEMANTIC_NETRUAL);
46
+ root.style.setProperty('--handle-background', $config.SEMANTIC_NEUTRAL);
47
47
  }, []);
48
48
 
49
49
  useEffect(() => {
@@ -149,7 +149,7 @@ const SwitchCameraIcon = (props: SwitchCameraIconProps) => {
149
149
  style={[
150
150
  styles.iconText,
151
151
  {
152
- color: disabled ? $config.SEMANTIC_NETRUAL : $config.FONT_COLOR,
152
+ color: disabled ? $config.SEMANTIC_NEUTRAL : $config.FONT_COLOR,
153
153
  },
154
154
  ]}>
155
155
  Switch
@@ -158,7 +158,7 @@ const SwitchCameraIcon = (props: SwitchCameraIconProps) => {
158
158
  style={[
159
159
  styles.iconText,
160
160
  {
161
- color: disabled ? $config.SEMANTIC_NETRUAL : $config.FONT_COLOR,
161
+ color: disabled ? $config.SEMANTIC_NEUTRAL : $config.FONT_COLOR,
162
162
  marginTop: 0,
163
163
  },
164
164
  ]}>
@@ -28,7 +28,7 @@ const styles = StyleSheet.create({
28
28
  paddingVertical: 0,
29
29
  },
30
30
  handleIndicatorStyle: {
31
- backgroundColor: $config.SEMANTIC_NETRUAL,
31
+ backgroundColor: $config.SEMANTIC_NEUTRAL,
32
32
  width: 40,
33
33
  height: 4,
34
34
  alignSelf: 'center',
@@ -28,48 +28,50 @@ const VideoCallMobileView = () => {
28
28
 
29
29
  const isCamON = useRef(local.video);
30
30
 
31
- useEffect(() => {
32
- if ($config.AUDIO_ROOM) return;
33
- const subscription = AppState.addEventListener(
34
- 'change',
35
- async (nextAppState) => {
36
- if (nextAppState === 'background') {
37
- // check if cam was on before app goes to background
38
- isCamON.current = isAndroid()
39
- ? local.video === ToggleState.enabled
40
- : RtcEngine?.isVideoEnabled;
31
+ // moved below logic to useMuteToggleLocal
32
+ // useEffect(() => {
33
+ // if ($config.AUDIO_ROOM) return;
34
+ // const subscription = AppState.addEventListener(
35
+ // 'change',
36
+ // async (nextAppState) => {
37
+ // if (nextAppState === 'background') {
38
+ // // check if cam was on before app goes to background
39
+ // isCamON.current = isAndroid()
40
+ // ? local.video === ToggleState.enabled
41
+ // : RtcEngine?.isVideoEnabled;
41
42
 
42
- if (isCamON.current) {
43
- isWebInternal()
44
- ? await RtcEngine.muteLocalVideoStream(true)
45
- : await RtcEngine.enableLocalVideo(false);
43
+ // if (isCamON.current || 1) {
44
+ // isWebInternal()
45
+ // ? await RtcEngine.muteLocalVideoStream(true)
46
+ // : await RtcEngine.enableLocalVideo(false);
46
47
 
47
- dispatch({
48
- type: 'LocalMuteVideo',
49
- value: [0],
50
- });
51
- }
52
- }
53
- if (nextAppState === 'active') {
54
- // enable cam only if cam was on before app goes to background
55
- if (isCamON.current) {
56
- isWebInternal()
57
- ? await RtcEngine.muteLocalVideoStream(false)
58
- : await RtcEngine.enableLocalVideo(true);
59
- dispatch({
60
- type: 'LocalMuteVideo',
61
- value: [1],
62
- });
63
- }
64
- }
65
- appState.current = nextAppState;
66
- },
67
- );
48
+ // // dispatch({
49
+ // // type: 'LocalMuteVideo',
50
+ // // value: [0],
51
+ // // });
52
+ // }
53
+ // }
54
+ // if (nextAppState === 'active') {
55
+ // // enable cam only if cam was on before app goes to background
56
+ // console.log('active state 111111 ==>', isCamON.current);
57
+ // if (local.video) {
58
+ // isWebInternal()
59
+ // ? await RtcEngine.muteLocalVideoStream(false)
60
+ // : await RtcEngine.enableLocalVideo(true);
61
+ // dispatch({
62
+ // type: 'LocalMuteVideo',
63
+ // value: [1],
64
+ // });
65
+ // }
66
+ // }
67
+ // appState.current = nextAppState;
68
+ // },
69
+ // );
68
70
 
69
- return () => {
70
- subscription?.remove();
71
- };
72
- }, []);
71
+ // return () => {
72
+ // subscription?.remove();
73
+ // };
74
+ // }, []);
73
75
 
74
76
  return (
75
77
  <View style={styles.container}>
@@ -28,6 +28,11 @@ const VideoRenderer: React.FC<VideoRendererProps> = ({user, isMax = false}) => {
28
28
  const {currentLayout} = useLayout();
29
29
  const showReplacePin =
30
30
  pinnedUid && !isMax && isHovered && currentLayout === getPinnedLayoutName();
31
+ const showPinForMe =
32
+ !pinnedUid &&
33
+ !isMax &&
34
+ isHovered &&
35
+ currentLayout === getPinnedLayoutName();
31
36
  const [videoTileWidth, setVideoTileWidth] = useState(0);
32
37
  const [avatarSize, setAvatarSize] = useState(100);
33
38
  return (
@@ -49,14 +54,16 @@ const VideoRenderer: React.FC<VideoRendererProps> = ({user, isMax = false}) => {
49
54
  ? maxStyle.noVideoStyle
50
55
  : maxStyle.nonActiveContainerStyle,
51
56
  ]}>
52
- {!showReplacePin && <ScreenShareNotice uid={user.uid} isMax={isMax} />}
57
+ {!showReplacePin && !showPinForMe && (
58
+ <ScreenShareNotice uid={user.uid} isMax={isMax} />
59
+ )}
53
60
  <NetworkQualityPill user={user} />
54
61
  <MaxVideoView
55
62
  fallback={() => {
56
63
  return FallbackLogo(
57
64
  user?.name,
58
65
  activeSpeaker,
59
- showReplacePin && !isMobileUA() ? true : false,
66
+ (showReplacePin || showPinForMe) && !isMobileUA() ? true : false,
60
67
  isMax,
61
68
  avatarSize,
62
69
  );
@@ -84,14 +91,14 @@ const VideoRenderer: React.FC<VideoRendererProps> = ({user, isMax = false}) => {
84
91
  ) : (
85
92
  <></>
86
93
  )}
87
- {showReplacePin && !isMobileUA() ? (
94
+ {(showReplacePin || showPinForMe) && !isMobileUA() ? (
88
95
  <IconButton
89
96
  onPress={() => {
90
97
  dispatch({type: 'UserPin', value: [user.uid]});
91
98
  }}
92
99
  containerStyle={maxStyle.replacePinContainer}
93
100
  btnTextProps={{
94
- text: 'Replace Pin',
101
+ text: showReplacePin ? 'Replace Pin' : 'Pin for me',
95
102
  textColor: $config.VIDEO_AUDIO_TILE_TEXT_COLOR,
96
103
  textStyle: {
97
104
  marginTop: 0,
@@ -53,7 +53,7 @@ export const ChatSendButton = (props: ChatSendButtonProps) => {
53
53
  tintColor={
54
54
  inputActive
55
55
  ? $config.PRIMARY_ACTION_BRAND_COLOR
56
- : $config.SEMANTIC_NETRUAL
56
+ : $config.SEMANTIC_NEUTRAL
57
57
  }
58
58
  name={'send'}
59
59
  />
@@ -52,7 +52,7 @@ export const ChatSendButton = (props: ChatSendButtonProps) => {
52
52
  tintColor={
53
53
  inputActive
54
54
  ? $config.PRIMARY_ACTION_BRAND_COLOR
55
- : $config.SEMANTIC_NETRUAL
55
+ : $config.SEMANTIC_NEUTRAL
56
56
  }
57
57
  name={'send'}
58
58
  />
@@ -22,9 +22,9 @@ const Illustration = () => {
22
22
  resizeMode={'contain'}
23
23
  source={{
24
24
  uri:
25
- $config.BACKGROUND_IMAGE === ''
25
+ $config.BG === ''
26
26
  ? 'https://gist.githubusercontent.com/EkaanshArora/59ae6969456f8e95f9752a4adf96bb44/raw/4c3831d115b4f9de0219e8658f049927b0ed9271/image.svg'
27
- : $config.BACKGROUND_IMAGE,
27
+ : $config.BG,
28
28
  }}
29
29
  />
30
30
  );
@@ -88,9 +88,9 @@ function LocalAudioMute(props: LocalAudioMuteProps) {
88
88
  tintColor: isAudioEnabled
89
89
  ? $config.PRIMARY_ACTION_TEXT_COLOR
90
90
  : disabled
91
- ? $config.SEMANTIC_NETRUAL
91
+ ? $config.SEMANTIC_NEUTRAL
92
92
  : permissionDenied
93
- ? $config.SEMANTIC_NETRUAL
93
+ ? $config.SEMANTIC_NEUTRAL
94
94
  : $config.SEMANTIC_ERROR,
95
95
  ...(props?.iconProps
96
96
  ? props.iconProps(isAudioEnabled, permissionDenied)
@@ -144,7 +144,7 @@ function LocalAudioMute(props: LocalAudioMuteProps) {
144
144
  iconButtonProps.iconProps = {
145
145
  ...iconButtonProps.iconProps,
146
146
  name: 'mic-off',
147
- tintColor: $config.SEMANTIC_NETRUAL,
147
+ tintColor: $config.SEMANTIC_NEUTRAL,
148
148
  };
149
149
  iconButtonProps.toolTipMessage = showToolTip
150
150
  ? isHandRaised(local.uid)
@@ -31,7 +31,7 @@ function LocalSwitchCamera(props: LocalSwitchCameraProps) {
31
31
  tintColor:
32
32
  isVideoEnabled || !disabled
33
33
  ? $config.PRIMARY_ACTION_TEXT_COLOR
34
- : $config.SEMANTIC_NETRUAL,
34
+ : $config.SEMANTIC_NEUTRAL,
35
35
  },
36
36
  disabled: !isVideoEnabled || disabled ? true : false,
37
37
  onPress: onPress,
@@ -86,9 +86,9 @@ function LocalVideoMute(props: LocalVideoMuteProps) {
86
86
  tintColor: isVideoEnabled
87
87
  ? $config.PRIMARY_ACTION_TEXT_COLOR
88
88
  : disabled
89
- ? $config.SEMANTIC_NETRUAL
89
+ ? $config.SEMANTIC_NEUTRAL
90
90
  : permissionDenied
91
- ? $config.SEMANTIC_NETRUAL
91
+ ? $config.SEMANTIC_NEUTRAL
92
92
  : $config.SEMANTIC_ERROR,
93
93
  ...(props?.iconProps
94
94
  ? props.iconProps(isVideoEnabled, permissionDenied)
@@ -141,7 +141,7 @@ function LocalVideoMute(props: LocalVideoMuteProps) {
141
141
  iconButtonProps.iconProps = {
142
142
  ...iconButtonProps.iconProps,
143
143
  name: 'video-off',
144
- tintColor: $config.SEMANTIC_NETRUAL,
144
+ tintColor: $config.SEMANTIC_NEUTRAL,
145
145
  };
146
146
  iconButtonProps.toolTipMessage = showToolTip
147
147
  ? isHandRaised(local.uid)
@@ -106,7 +106,7 @@ const RemoteAudioMute = (props: RemoteAudioMuteProps) => {
106
106
  name: props.audio ? 'mic-on' : 'mic-off',
107
107
  tintColor: props.audio
108
108
  ? $config.PRIMARY_ACTION_BRAND_COLOR
109
- : $config.SEMANTIC_NETRUAL,
109
+ : $config.SEMANTIC_NEUTRAL,
110
110
  }}
111
111
  />
112
112
  </>
@@ -93,7 +93,7 @@ const RemoteVideoMute = (props: RemoteVideoMuteProps) => {
93
93
  iconType: 'plain',
94
94
  tintColor: props.video
95
95
  ? $config.PRIMARY_ACTION_BRAND_COLOR
96
- : $config.SEMANTIC_NETRUAL,
96
+ : $config.SEMANTIC_NEUTRAL,
97
97
  }}
98
98
  />
99
99
  </>
@@ -210,7 +210,7 @@ const style = StyleSheet.create({
210
210
  flexShrink: 1,
211
211
  },
212
212
  chatNotificationPrivate: {
213
- backgroundColor: $config.SEMANTIC_NETRUAL,
213
+ backgroundColor: $config.SEMANTIC_NEUTRAL,
214
214
  borderRadius: 8,
215
215
  alignSelf: 'center',
216
216
  },
@@ -74,7 +74,7 @@ const ScreenshareButton = (props: ScreenshareButtonProps) => {
74
74
  ) {
75
75
  iconButtonProps.iconProps = {
76
76
  ...iconButtonProps.iconProps,
77
- tintColor: $config.SEMANTIC_NETRUAL,
77
+ tintColor: $config.SEMANTIC_NEUTRAL,
78
78
  };
79
79
  iconButtonProps.toolTipMessage = isHandRaised(local.uid)
80
80
  ? 'Waiting for host to appove the request'
@@ -10,8 +10,11 @@
10
10
  *********************************************
11
11
  */
12
12
  import {useLocalUserInfo, useRtc} from 'customization-api';
13
+ import {useContext, useEffect, useRef, useState} from 'react';
14
+
13
15
  import {ToggleState} from '../../agora-rn-uikit/src/Contexts/PropsContext';
14
16
  import {isWebInternal} from './common';
17
+ import {AppState} from 'react-native';
15
18
 
16
19
  export enum MUTE_LOCAL_TYPE {
17
20
  audio,
@@ -24,6 +27,48 @@ function useMuteToggleLocal() {
24
27
  const {RtcEngine, dispatch} = useRtc();
25
28
  const local = useLocalUserInfo();
26
29
 
30
+ const appState = useRef(AppState.currentState);
31
+ const [appStateVisible, setAppStateVisible] = useState(appState.current);
32
+ const isCamON = useRef(local.video);
33
+
34
+ useEffect(() => {
35
+ if ($config.AUDIO_ROOM) return;
36
+ const subscription = AppState.addEventListener('change', (nextAppState) => {
37
+ appState.current = nextAppState;
38
+ setAppStateVisible(appState.current);
39
+ });
40
+
41
+ return () => {
42
+ subscription?.remove();
43
+ };
44
+ }, []);
45
+
46
+ useEffect(() => {
47
+ // console.log(`Video State ${local.video} in Mode ${appStateVisible}`);
48
+ if (appStateVisible === 'background') {
49
+ isCamON.current = local.video;
50
+ if (isCamON.current) {
51
+ isWebInternal()
52
+ ? RtcEngine.muteLocalVideoStream(true)
53
+ : RtcEngine.enableLocalVideo(false);
54
+
55
+ dispatch({
56
+ type: 'LocalMuteVideo',
57
+ value: [0],
58
+ });
59
+ }
60
+ }
61
+ if (appStateVisible === 'active' && isCamON.current) {
62
+ isWebInternal()
63
+ ? RtcEngine.muteLocalVideoStream(false)
64
+ : RtcEngine.enableLocalVideo(true);
65
+ dispatch({
66
+ type: 'LocalMuteVideo',
67
+ value: [1],
68
+ });
69
+ }
70
+ }, [appStateVisible]);
71
+
27
72
  return async (type: MUTE_LOCAL_TYPE) => {
28
73
  switch (type) {
29
74
  case MUTE_LOCAL_TYPE.audio: