agora-appbuilder-core 4.1.9 → 4.1.10-beta.1

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 (107) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -3
  3. package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -2
  4. package/template/agora-rn-uikit/src/Reducer/index.ts +0 -2
  5. package/template/agora-rn-uikit/src/Rtc/Join.tsx +11 -25
  6. package/template/agora-rn-uikit/src/RtcConfigure.tsx +1 -14
  7. package/template/bridge/rtc/webNg/RtcEngine.ts +2 -2
  8. package/template/bridge/rtm/web/index.ts +30 -0
  9. package/template/customization-api/typeDefinition.ts +1 -0
  10. package/template/defaultConfig.js +3 -2
  11. package/template/global.d.ts +1 -0
  12. package/template/src/AppRoutes.tsx +3 -3
  13. package/template/src/ai-agent/components/ControlButtons.tsx +1 -1
  14. package/template/src/assets/font-styles.css +36 -0
  15. package/template/src/assets/fonts/icomoon.ttf +0 -0
  16. package/template/src/assets/selection.json +1 -1
  17. package/template/src/atoms/CustomIcon.tsx +8 -0
  18. package/template/src/atoms/Dropdown.tsx +5 -0
  19. package/template/src/atoms/TertiaryButton.tsx +1 -1
  20. package/template/src/atoms/UserAvatar.tsx +1 -1
  21. package/template/src/components/ChatContext.ts +5 -3
  22. package/template/src/components/Controls.tsx +68 -22
  23. package/template/src/components/DeviceConfigure.tsx +1 -1
  24. package/template/src/components/EventsConfigure.tsx +22 -17
  25. package/template/src/components/Navbar.tsx +14 -11
  26. package/template/src/components/RTMConfigure.tsx +31 -1036
  27. package/template/src/components/UserGlobalPreferenceProvider.tsx +227 -0
  28. package/template/src/components/beauty-effect/useBeautyEffects.tsx +50 -13
  29. package/template/src/components/breakout-room/BreakoutRoomPanel.tsx +58 -0
  30. package/template/src/components/breakout-room/context/BreakoutRoomContext.tsx +2508 -0
  31. package/template/src/components/breakout-room/events/BreakoutRoomEventsConfigure.tsx +272 -0
  32. package/template/src/components/breakout-room/events/constants.ts +17 -0
  33. package/template/src/components/breakout-room/hoc/BreakoutRoomNameRenderer.tsx +68 -0
  34. package/template/src/components/breakout-room/hooks/useBreakoutRoomExit.ts +49 -0
  35. package/template/src/components/breakout-room/state/reducer.ts +522 -0
  36. package/template/src/components/breakout-room/state/types.ts +54 -0
  37. package/template/src/components/breakout-room/ui/BreakoutMeetingTitle.tsx +60 -0
  38. package/template/src/components/breakout-room/ui/BreakoutRoomActionMenu.tsx +136 -0
  39. package/template/src/components/breakout-room/ui/BreakoutRoomAnnouncementModal.tsx +135 -0
  40. package/template/src/components/breakout-room/ui/BreakoutRoomGroupSettings.tsx +588 -0
  41. package/template/src/components/breakout-room/ui/BreakoutRoomMainRoomUsers.tsx +142 -0
  42. package/template/src/components/breakout-room/ui/BreakoutRoomMemberActionMenu.tsx +122 -0
  43. package/template/src/components/breakout-room/ui/BreakoutRoomParticipants.tsx +124 -0
  44. package/template/src/components/breakout-room/ui/BreakoutRoomRaiseHand.tsx +65 -0
  45. package/template/src/components/breakout-room/ui/BreakoutRoomRenameModal.tsx +227 -0
  46. package/template/src/components/breakout-room/ui/BreakoutRoomSettings.tsx +140 -0
  47. package/template/src/components/breakout-room/ui/BreakoutRoomTransition.tsx +52 -0
  48. package/template/src/components/breakout-room/ui/BreakoutRoomView.tsx +193 -0
  49. package/template/src/components/breakout-room/ui/ExitBreakoutRoomIconButton.tsx +79 -0
  50. package/template/src/components/breakout-room/ui/ParticipantManualAssignmentModal.tsx +638 -0
  51. package/template/src/components/breakout-room/ui/SelectParticipantAssignmentStrategy.tsx +57 -0
  52. package/template/src/components/chat/chatConfigure.tsx +7 -1
  53. package/template/src/components/chat-messages/useChatMessages.tsx +43 -11
  54. package/template/src/components/common/Dividers.tsx +53 -0
  55. package/template/src/components/controls/toolbar-items/ExitBreakoutRoomToolbarItem.tsx +13 -0
  56. package/template/src/components/controls/useControlPermissionMatrix.tsx +32 -4
  57. package/template/src/components/participants/AllHostParticipants.tsx +10 -2
  58. package/template/src/components/participants/Participant.tsx +7 -1
  59. package/template/src/components/participants/UserActionMenuOptions.tsx +12 -2
  60. package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +12 -8
  61. package/template/src/components/precall/joinWaitingRoomBtn.tsx +14 -10
  62. package/template/src/components/raise-hand/RaiseHandButton.tsx +50 -0
  63. package/template/src/components/raise-hand/RaiseHandProvider.tsx +308 -0
  64. package/template/src/components/raise-hand/index.ts +14 -0
  65. package/template/src/components/recordings/RecordingsDateTable.tsx +3 -2
  66. package/template/src/components/room-info/useCurrentRoomInfo.tsx +42 -0
  67. package/template/src/components/room-info/useSetBreakoutRoomInfo.tsx +64 -0
  68. package/template/src/components/useUserPreference.tsx +39 -12
  69. package/template/src/components/virtual-background/useVB.tsx +18 -0
  70. package/template/src/components/whiteboard/WhiteboardConfigure.tsx +27 -0
  71. package/template/src/language/default-labels/videoCallScreenLabels.ts +7 -0
  72. package/template/src/logger/AppBuilderLogger.tsx +11 -3
  73. package/template/src/pages/VideoCall.tsx +171 -518
  74. package/template/src/pages/video-call/BreakoutVideoCall.tsx +213 -0
  75. package/template/src/pages/video-call/SidePanelHeader.tsx +17 -0
  76. package/template/src/pages/video-call/VideoCallContent.tsx +211 -0
  77. package/template/src/pages/video-call/VideoCallScreen.tsx +18 -0
  78. package/template/src/pages/video-call/VideoCallScreenWrapper.tsx +0 -1
  79. package/template/src/pages/video-call/VideoCallStateWrapper.tsx +495 -0
  80. package/template/src/rtm/RTMConfigureBreakoutRoomProvider.tsx +882 -0
  81. package/template/src/rtm/RTMConfigureMainRoomProvider.tsx +757 -0
  82. package/template/src/rtm/RTMCoreProvider.tsx +419 -0
  83. package/template/src/rtm/RTMEngine.ts +188 -60
  84. package/template/src/rtm/RTMGlobalStateProvider.tsx +706 -0
  85. package/template/src/rtm/RTMStatusBanner.tsx +99 -0
  86. package/template/src/rtm/constants.ts +12 -0
  87. package/template/src/rtm/hooks/useMainRoomUserDisplayName.ts +45 -0
  88. package/template/src/rtm/rtm-presence-utils.ts +344 -0
  89. package/template/src/rtm/utils.ts +68 -1
  90. package/template/src/rtm-events/constants.ts +40 -1
  91. package/template/src/rtm-events-api/Events.ts +62 -19
  92. package/template/src/subComponents/ChatBubble.tsx +3 -3
  93. package/template/src/subComponents/ChatContainer.tsx +19 -9
  94. package/template/src/subComponents/LocalAudioMute.tsx +2 -2
  95. package/template/src/subComponents/LocalVideoMute.tsx +2 -2
  96. package/template/src/subComponents/SidePanelEnum.tsx +1 -0
  97. package/template/src/subComponents/caption/useCaption.tsx +1 -1
  98. package/template/src/subComponents/chat/ChatAnnouncementView.tsx +65 -0
  99. package/template/src/subComponents/chat/ChatSendButton.tsx +1 -0
  100. package/template/src/subComponents/screenshare/ScreenshareButton.tsx +16 -0
  101. package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +1 -1
  102. package/template/src/subComponents/waiting-rooms/WaitingRoomControls.tsx +7 -4
  103. package/template/src/utils/useDebouncedCallback.tsx +20 -0
  104. package/template/src/utils/useEndCall.ts +0 -2
  105. package/template/src/utils/useMuteToggleLocal.ts +14 -10
  106. package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +0 -11
  107. package/template/agora-rn-uikit/src/Reducer/UserBanned.ts +0 -11
@@ -13,7 +13,12 @@
13
13
  ('use strict');
14
14
  import {type RTMClient} from 'agora-react-native-rtm';
15
15
  import RTMEngine from '../rtm/RTMEngine';
16
- import {EventUtils} from '../rtm-events';
16
+ import {
17
+ EventUtils,
18
+ RTM_EVENT_SCOPE,
19
+ RTM_GLOBAL_SCOPE_EVENTS,
20
+ RTM_SESSION_SCOPE_EVENTS,
21
+ } from '../rtm-events';
17
22
  import {
18
23
  ReceiverUid,
19
24
  EventCallback,
@@ -25,6 +30,16 @@ import {adjustUID} from '../rtm/utils';
25
30
  import {LogSource, logger} from '../logger/AppBuilderLogger';
26
31
  import {nativeChannelTypeMapping} from '../../bridge/rtm/web/Types';
27
32
 
33
+ function getRTMEventScope(eventName: string): RTM_EVENT_SCOPE {
34
+ if (RTM_GLOBAL_SCOPE_EVENTS.includes(eventName)) {
35
+ return RTM_EVENT_SCOPE.GLOBAL;
36
+ }
37
+ if (RTM_SESSION_SCOPE_EVENTS.includes(eventName)) {
38
+ return RTM_EVENT_SCOPE.SESSION;
39
+ }
40
+ return RTM_EVENT_SCOPE.LOCAL;
41
+ }
42
+
28
43
  class Events {
29
44
  private source: EventSource = EventSource.core;
30
45
 
@@ -41,10 +56,16 @@ class Events {
41
56
  * @param {String} payload to be stored in rtm Attribute value
42
57
  * @api private
43
58
  */
44
- private _persist = async (evt: string, payload: string) => {
59
+ private _persist = async (evt: string, payload: string, roomKey?: string) => {
45
60
  const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
46
61
  const userId = RTMEngine.getInstance().localUid;
47
62
  try {
63
+ // const roomAwareKey = roomKey ? `${roomKey}__${evt}` : evt;
64
+ // console.log(
65
+ // 'session-attributes setting roomAwareKey as: ',
66
+ // roomAwareKey,
67
+ // evt,
68
+ // );
48
69
  const rtmAttribute = {key: evt, value: payload};
49
70
  // Step 1: Call RTM API to update local attributes
50
71
  await rtmEngine.storage.setUserMetadata(
@@ -104,11 +125,13 @@ class Events {
104
125
  *
105
126
  * @param {Object} rtmPayload payload to be sent across
106
127
  * @param {ReceiverUid} toUid uid or uids[] of user
128
+ * @param {string} channelId optional specific channel ID, defaults to primary channel
107
129
  * @api private
108
130
  */
109
131
  private _send = async (
110
132
  rtmPayload: RTMAttributePayload,
111
133
  toUid?: ReceiverUid,
134
+ toChannelId?: string,
112
135
  ) => {
113
136
  const to = typeof toUid === 'string' ? parseInt(toUid, 10) : toUid;
114
137
 
@@ -131,16 +154,21 @@ class Events {
131
154
  'case 1 executed - sending in channel',
132
155
  );
133
156
  try {
134
- const channelId = RTMEngine.getInstance().channelUid;
135
- if (!channelId || channelId.trim() === '') {
157
+ logger.debug(
158
+ LogSource.Events,
159
+ 'CUSTOM_EVENTS',
160
+ 'event is sent to targetChannelId ->',
161
+ toChannelId,
162
+ );
163
+ if (!toChannelId || toChannelId.trim() === '') {
136
164
  throw new Error(
137
- 'Channel ID is not set. Cannot send channel attributes.',
165
+ 'Channel ID is not set. Cannot send channel messages.',
138
166
  );
139
167
  }
140
- await rtmEngine.publish(channelId, text, {
168
+ await rtmEngine.publish(toChannelId, text, {
141
169
  channelType: nativeChannelTypeMapping.MESSAGE, // 1 is message
142
- customType: 'PlainText',
143
- messageType: 1,
170
+ // customType: 'PlainText',
171
+ // messageType: RtmMessageType.string,
144
172
  });
145
173
  } catch (error) {
146
174
  logger.error(
@@ -223,7 +251,10 @@ class Events {
223
251
  }
224
252
  };
225
253
 
226
- private _sendAsChannelAttribute = async (rtmPayload: RTMAttributePayload) => {
254
+ private _sendAsChannelAttribute = async (
255
+ rtmPayload: RTMAttributePayload,
256
+ toChannelId?: string,
257
+ ) => {
227
258
  // Case 1: send to channel
228
259
  logger.debug(
229
260
  LogSource.Events,
@@ -237,16 +268,13 @@ class Events {
237
268
  }
238
269
  const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
239
270
 
240
- const channelId = RTMEngine.getInstance().channelUid;
241
- if (!channelId || channelId.trim() === '') {
242
- throw new Error(
243
- 'Channel ID is not set. Cannot send channel attributes.',
244
- );
271
+ if (!toChannelId) {
272
+ throw new Error('Channel ID is not set. Cannot send channel messages.');
245
273
  }
246
274
 
247
275
  const rtmAttribute = [{key: rtmPayload.evt, value: rtmPayload.value}];
248
276
  await rtmEngine.storage.setChannelMetadata(
249
- channelId,
277
+ toChannelId,
250
278
  nativeChannelTypeMapping.MESSAGE,
251
279
  {
252
280
  items: rtmAttribute,
@@ -351,6 +379,7 @@ class Events {
351
379
  * @param {String} payload (optional) Additional data to be sent along with the event.
352
380
  * @param {Enum} persistLevel (optional) set different levels of persistance. Default value is Level 1
353
381
  * @param {ReceiverUid} receiver (optional) uid or uid array. Default mode sends message in channel.
382
+ * @param {String} channelId (optional) specific channel to send to, defaults to primary channel.
354
383
  * @api public
355
384
  * */
356
385
  send = async (
@@ -358,6 +387,7 @@ class Events {
358
387
  payload: string = '',
359
388
  persistLevel: PersistanceLevel = PersistanceLevel.None,
360
389
  receiver: ReceiverUid = -1,
390
+ toChannelId?: string,
361
391
  ) => {
362
392
  try {
363
393
  if (!this._validateEvt(eventName)) {
@@ -373,12 +403,18 @@ class Events {
373
403
  return; // Don't throw - just log and return
374
404
  }
375
405
 
406
+ // Add meta data
407
+ let currentEventScope = getRTMEventScope(eventName);
408
+ let currentChannelId = RTMEngine.getInstance().getActiveChannelId();
409
+ let currentRoomKey = RTMEngine.getInstance().getActiveChannelName();
410
+
376
411
  const persistValue = JSON.stringify({
377
412
  payload,
378
413
  persistLevel,
379
414
  source: this.source,
415
+ _scope: currentEventScope,
416
+ _channelId: currentChannelId,
380
417
  });
381
-
382
418
  const rtmPayload: RTMAttributePayload = {
383
419
  evt: eventName,
384
420
  value: persistValue,
@@ -389,7 +425,13 @@ class Events {
389
425
  persistLevel === PersistanceLevel.Session
390
426
  ) {
391
427
  try {
392
- await this._persist(eventName, persistValue);
428
+ await this._persist(
429
+ eventName,
430
+ persistValue,
431
+ persistLevel === PersistanceLevel.Session
432
+ ? currentRoomKey
433
+ : undefined,
434
+ );
393
435
  } catch (error) {
394
436
  logger.error(LogSource.Events, 'CUSTOM_EVENTS', 'persist error', error);
395
437
  // don't throw - just log the error, application should continue running
@@ -402,10 +444,11 @@ class Events {
402
444
  `sending event -> ${eventName}`,
403
445
  persistValue,
404
446
  );
447
+ const targetChannelId = toChannelId || currentChannelId;
405
448
  if (persistLevel === PersistanceLevel.Channel) {
406
- await this._sendAsChannelAttribute(rtmPayload);
449
+ await this._sendAsChannelAttribute(rtmPayload, targetChannelId);
407
450
  } else {
408
- await this._send(rtmPayload, receiver);
451
+ await this._send(rtmPayload, receiver, targetChannelId);
409
452
  }
410
453
  } catch (error) {
411
454
  logger.error(
@@ -52,6 +52,7 @@ import {useChatConfigure} from '../../src/components/chat/chatConfigure';
52
52
  import Tooltip from '../../src/atoms/Tooltip';
53
53
  import {MoreMessageOptions} from './chat/ChatQuickActionsMenu';
54
54
  import {EMessageStatus} from '../../src/ai-agent/components/AgentControls/message';
55
+ import {useMainRoomUserDisplayName} from '../rtm/hooks/useMainRoomUserDisplayName';
55
56
 
56
57
  type AttachmentBubbleProps = {
57
58
  fileName: string;
@@ -362,6 +363,7 @@ const ChatBubble = (props: ChatBubbleProps) => {
362
363
  //commented for v1 release
363
364
  //const remoteUserDefaultLabel = useString('remoteUserDefaultLabel')();
364
365
  const remoteUserDefaultLabel = useString(videoRoomUserFallbackText)();
366
+ const getDisplayName = useMainRoomUserDisplayName();
365
367
 
366
368
  const getUsername = () => {
367
369
  if (isLocal) {
@@ -370,9 +372,7 @@ const ChatBubble = (props: ChatBubbleProps) => {
370
372
  if (remoteUIConfig?.username) {
371
373
  return trimText(remoteUIConfig?.username);
372
374
  }
373
- return defaultContent[uid]?.name
374
- ? trimText(defaultContent[uid].name)
375
- : remoteUserDefaultLabel;
375
+ return getDisplayName(uid);
376
376
  };
377
377
 
378
378
  return props?.render ? (
@@ -27,7 +27,7 @@ import {
27
27
  } from 'react-native';
28
28
  import {RFValue} from 'react-native-responsive-fontsize';
29
29
  import ChatBubble from './ChatBubble';
30
- import {ChatBubbleProps} from '../components/ChatContext';
30
+ import ChatContext, {ChatBubbleProps} from '../components/ChatContext';
31
31
  import {
32
32
  DispatchContext,
33
33
  ContentInterface,
@@ -59,6 +59,7 @@ import {
59
59
  } from '../language/default-labels/videoCallScreenLabels';
60
60
  import CommonStyles from '../components/CommonStyles';
61
61
  import PinnedMessage from './chat/PinnedMessage';
62
+ import ChatAnnouncementView from './chat/ChatAnnouncementView';
62
63
 
63
64
  /**
64
65
  * Chat container is the component which renders all the chat messages
@@ -71,6 +72,7 @@ const ChatContainer = (props?: {
71
72
  const info1 = useString<boolean>(groupChatWelcomeContent);
72
73
  const [scrollToEnd, setScrollToEnd] = useState(false);
73
74
  const {dispatch} = useContext(DispatchContext);
75
+ const {syncUserState} = useContext(ChatContext);
74
76
  const [grpUnreadCount, setGrpUnreadCount] = useState(0);
75
77
  const [privateUnreadCount, setPrivateUnreadCount] = useState(0);
76
78
  const {defaultContent} = useContent();
@@ -118,16 +120,18 @@ const ChatContainer = (props?: {
118
120
  });
119
121
  //Once message is seen, reset lastMessageTimeStamp.
120
122
  //so whoever has unread count will show in the top of participant list
121
- updateRenderListState(privateChatUser, {lastMessageTimeStamp: 0});
123
+ // updateRenderListState(privateChatUser, {lastMessageTimeStamp: 0});
124
+ syncUserState(privateChatUser, {lastMessageTimeStamp: 0});
122
125
  }
123
126
  }, [privateChatUser]);
124
127
 
125
- const updateRenderListState = (
126
- uid: number,
127
- data: Partial<ContentInterface>,
128
- ) => {
129
- dispatch({type: 'UpdateRenderList', value: [uid, data]});
130
- };
128
+ // We will be using syncRoomusers
129
+ // const updateRenderListState = (
130
+ // uid: number,
131
+ // data: Partial<ContentInterface>,
132
+ // ) => {
133
+ // dispatch({type: 'UpdateRenderList', value: [uid, data]});
134
+ // };
131
135
 
132
136
  const onScroll = event => {
133
137
  setScrollOffset(event.nativeEvent.contentOffset.y);
@@ -249,7 +253,13 @@ const ChatContainer = (props?: {
249
253
  </Text>
250
254
  </View>
251
255
  ) : null}
252
- {!message?.hide ? (
256
+
257
+ {message?.announcement ? (
258
+ <ChatAnnouncementView
259
+ message={message.msg}
260
+ announcement={message.announcement}
261
+ />
262
+ ) : !message?.hide ? (
253
263
  <ChatBubbleComponent
254
264
  isLocal={localUid === message.uid}
255
265
  isSameUser={
@@ -85,7 +85,7 @@ function LocalAudioMute(props: LocalAudioMuteProps) {
85
85
  local.permissionStatus === PermissionState.REJECTED ||
86
86
  local.permissionStatus === PermissionState.GRANTED_FOR_CAM_ONLY;
87
87
 
88
- const onPress = () => {
88
+ const onPress = async () => {
89
89
  logger.log(
90
90
  LogSource.Internals,
91
91
  'LOCAL_MUTE',
@@ -95,7 +95,7 @@ function LocalAudioMute(props: LocalAudioMuteProps) {
95
95
  permissionDenied,
96
96
  },
97
97
  );
98
- localMute(MUTE_LOCAL_TYPE.audio);
98
+ await localMute(MUTE_LOCAL_TYPE.audio);
99
99
  };
100
100
  const audioLabel = permissionDenied
101
101
  ? micButtonLabel(I18nDeviceStatus.PERMISSION_DENIED)
@@ -79,7 +79,7 @@ function LocalVideoMute(props: LocalVideoMuteProps) {
79
79
  );
80
80
 
81
81
  const lstooltip = useString<boolean>(livestreamingCameraTooltipText);
82
- const onPress = () => {
82
+ const onPress = async () => {
83
83
  //if screensharing is going on native - to turn on video screenshare should be turn off
84
84
  //show confirm popup to stop the screenshare
85
85
  logger.log(
@@ -91,7 +91,7 @@ function LocalVideoMute(props: LocalVideoMuteProps) {
91
91
  permissionDenied,
92
92
  },
93
93
  );
94
- localMute(MUTE_LOCAL_TYPE.video);
94
+ await localMute(MUTE_LOCAL_TYPE.video);
95
95
  };
96
96
  const isVideoEnabled = local.video === ToggleState.enabled;
97
97
 
@@ -16,4 +16,5 @@ export enum SidePanelType {
16
16
  Settings = 'Settings',
17
17
  Transcript = 'Transcript',
18
18
  VirtualBackground = 'VirtualBackground',
19
+ BreakoutRoom = 'BreakoutRoom',
19
20
  }
@@ -72,7 +72,7 @@ export const CaptionContext = React.createContext<{
72
72
  });
73
73
 
74
74
  interface CaptionProviderProps {
75
- callActive: boolean;
75
+ callActive?: boolean;
76
76
  children: React.ReactNode;
77
77
  }
78
78
 
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import {View, StyleSheet, Text} from 'react-native';
3
+ import ImageIcon from '../../atoms/ImageIcon';
4
+ import ThemeConfig from '../../theme';
5
+ import {AnnouncementMessage} from '../../components/chat-messages/useChatMessages';
6
+
7
+ export default function ChatAnnouncementView({
8
+ message,
9
+ announcement,
10
+ }: {
11
+ message: string;
12
+ announcement: AnnouncementMessage;
13
+ }) {
14
+ return (
15
+ <View style={style.announcementView}>
16
+ <ImageIcon
17
+ iconSize={20}
18
+ iconType="plain"
19
+ name="announcement"
20
+ tintColor={'#099DFD'}
21
+ />
22
+ <View style={style.announcementMessage}>
23
+ <Text style={style.announcementMessageHeader}>
24
+ Message from host: {announcement.sender}
25
+ </Text>
26
+ <Text style={style.announcementMessageBody}>{message}</Text>
27
+ </View>
28
+ </View>
29
+ );
30
+ }
31
+
32
+ const style = StyleSheet.create({
33
+ announcementView: {
34
+ display: 'flex',
35
+ flexDirection: 'row',
36
+ alignItems: 'flex-start',
37
+ justifyContent: 'flex-start',
38
+ marginRight: 12,
39
+ marginLeft: 12,
40
+ marginBottom: 4,
41
+ marginTop: 16,
42
+ gap: 6,
43
+ },
44
+ announcementMessage: {
45
+ display: 'flex',
46
+ flexDirection: 'column',
47
+ gap: 2,
48
+ },
49
+ announcementMessageHeader: {
50
+ fontSize: ThemeConfig.FontSize.tiny,
51
+ fontStyle: 'normal',
52
+ fontFamily: ThemeConfig.FontFamily.sansPro,
53
+ color: '#099DFD',
54
+ fontWeight: '700',
55
+ lineHeight: 14,
56
+ },
57
+ announcementMessageBody: {
58
+ fontSize: ThemeConfig.FontSize.tiny,
59
+ fontStyle: 'italic',
60
+ fontFamily: ThemeConfig.FontFamily.sansPro,
61
+ color: '#099DFD',
62
+ fontWeight: '400',
63
+ lineHeight: 14,
64
+ },
65
+ });
@@ -71,6 +71,7 @@ export const handleChatSend = ({
71
71
  return;
72
72
  }
73
73
 
74
+ // Text message sent update sender side ui
74
75
  const sendTextMessage = () => {
75
76
  const option = {
76
77
  chatType: selectedUserId
@@ -25,6 +25,7 @@ import {
25
25
  toolbarItemShareText,
26
26
  } from '../../language/default-labels/videoCallScreenLabels';
27
27
  import {useToolbarProps} from '../../atoms/ToolbarItem';
28
+ import {useBreakoutRoom} from '../../components/breakout-room/context/BreakoutRoomContext';
28
29
  /**
29
30
  * A component to start and stop screen sharing on web clients.
30
31
  * Screen sharing is not yet implemented on mobile platforms.
@@ -53,6 +54,11 @@ const ScreenshareButton = (props: ScreenshareButtonProps) => {
53
54
  const {setShowStartScreenSharePopup} = useVideoCall();
54
55
  const screenShareButtonLabel = useString<boolean>(toolbarItemShareText);
55
56
  const lstooltip = useString<boolean>(livestreamingShareTooltipText);
57
+ const {permissions} = useBreakoutRoom();
58
+ // In the main room (default case), permissions come from the main room state.
59
+ // If the user is in a breakout room, retrieve permissions from the breakout room instead.
60
+ const canScreenshareInBreakoutRoom = permissions.canScreenshare;
61
+
56
62
  const onPress = () => {
57
63
  if (isScreenshareActive) {
58
64
  stopScreenshare();
@@ -105,6 +111,16 @@ const ScreenshareButton = (props: ScreenshareButtonProps) => {
105
111
  iconButtonProps.disabled = true;
106
112
  }
107
113
 
114
+ if (!canScreenshareInBreakoutRoom) {
115
+ iconButtonProps.iconProps = {
116
+ ...iconButtonProps.iconProps,
117
+ tintColor: $config.SEMANTIC_NEUTRAL,
118
+ showWarningIcon: true,
119
+ };
120
+ iconButtonProps.toolTipMessage = 'cannot screenshare';
121
+ iconButtonProps.disabled = true;
122
+ }
123
+
108
124
  return props?.render ? (
109
125
  props.render(onPress, isScreenshareActive)
110
126
  ) : isToolbarMenuItem ? (
@@ -319,7 +319,7 @@ export const ScreenshareConfigure = (props: {children: React.ReactNode}) => {
319
319
  'Trying to start native screenshare',
320
320
  );
321
321
  if (video) {
322
- localMute(MUTE_LOCAL_TYPE.video);
322
+ await localMute(MUTE_LOCAL_TYPE.video);
323
323
  }
324
324
  try {
325
325
  await engine.current.startScreenCapture({
@@ -14,12 +14,14 @@ import {
14
14
  peoplePanelWaitingRoomRequestApprovalBtnTxt,
15
15
  peoplePanelWaitingRoomRequestDenyBtnTxt,
16
16
  } from '../../../src/language/default-labels/videoCallScreenLabels';
17
+ import ChatContext from '../../components/ChatContext';
17
18
 
18
19
  const WaitingRoomButton = props => {
19
20
  const {uid, screenUid, isAccept} = props;
20
21
  const {approval} = useWaitingRoomAPI();
21
22
  const localUid = useLocalUid();
22
23
  const {dispatch} = useContext(DispatchContext);
24
+ const {syncUserState} = useContext(ChatContext);
23
25
  const {waitingRoomRef} = useWaitingRoomContext();
24
26
  const admintext = useString(peoplePanelWaitingRoomRequestApprovalBtnTxt)();
25
27
  const denytext = useString(peoplePanelWaitingRoomRequestDenyBtnTxt)();
@@ -40,10 +42,11 @@ const WaitingRoomButton = props => {
40
42
  Toast.hide();
41
43
  }
42
44
 
43
- dispatch({
44
- type: 'UpdateRenderList',
45
- value: [uid, {isInWaitingRoom: false}],
46
- });
45
+ // dispatch({
46
+ // type: 'UpdateRenderList',
47
+ // value: [uid, {isInWaitingRoom: false}],
48
+ // });
49
+ syncUserState(uid, {isInWaitingRoom: false});
47
50
 
48
51
  if (waitingRoomRef.current) {
49
52
  waitingRoomRef.current[uid] = approved ? 'APPROVED' : 'REJECTED';
@@ -0,0 +1,20 @@
1
+ import {useRef, useCallback} from 'react';
2
+
3
+ export function useDebouncedCallback<T extends (...args: any[]) => void>(
4
+ fn: T,
5
+ delay: number,
6
+ ): (...args: Parameters<T>) => void {
7
+ const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
8
+
9
+ const debounced = useCallback(
10
+ (...args: Parameters<T>) => {
11
+ if (timeoutRef.current) {
12
+ clearTimeout(timeoutRef.current);
13
+ }
14
+ timeoutRef.current = setTimeout(() => fn(...args), delay);
15
+ },
16
+ [fn, delay],
17
+ );
18
+
19
+ return debounced;
20
+ }
@@ -9,7 +9,6 @@ import {
9
9
  import {PropsContext, DispatchContext} from '../../agora-rn-uikit';
10
10
  import {useHistory} from '../components/Router';
11
11
  import {stopForegroundService} from '../subComponents/LocalEndCall';
12
- import RTMEngine from '../rtm/RTMEngine';
13
12
  import {ENABLE_AUTH} from '../auth/config';
14
13
  import {useAuth} from '../auth/AuthProvider';
15
14
  import {useChatConfigure} from '../components/chat/chatConfigure';
@@ -69,7 +68,6 @@ const useEndCall = () => {
69
68
  if ($config.CHAT) {
70
69
  deleteChatUser();
71
70
  }
72
- RTMEngine.getInstance().engine.unsubscribe(rtcProps.channel);
73
71
  if (!ENABLE_AUTH) {
74
72
  // await authLogout();
75
73
  await authLogin();
@@ -86,14 +86,16 @@ function useMuteToggleLocal() {
86
86
  );
87
87
 
88
88
  // Enable UI
89
+ const newAudioState =
90
+ localAudioState === ToggleState.enabled
91
+ ? ToggleState.disabled
92
+ : ToggleState.enabled;
93
+
89
94
  dispatch({
90
95
  type: 'LocalMuteAudio',
91
- value: [
92
- localAudioState === ToggleState.enabled
93
- ? ToggleState.disabled
94
- : ToggleState.enabled,
95
- ],
96
+ value: [newAudioState],
96
97
  });
98
+
97
99
  handleQueue();
98
100
  } catch (e) {
99
101
  dispatch({
@@ -152,14 +154,16 @@ function useMuteToggleLocal() {
152
154
  );
153
155
  }
154
156
  // Enable UI
157
+ const newVideoState =
158
+ localVideoState === ToggleState.enabled
159
+ ? ToggleState.disabled
160
+ : ToggleState.enabled;
161
+
155
162
  dispatch({
156
163
  type: 'LocalMuteVideo',
157
- value: [
158
- localVideoState === ToggleState.enabled
159
- ? ToggleState.disabled
160
- : ToggleState.enabled,
161
- ],
164
+ value: [newVideoState],
162
165
  });
166
+
163
167
  handleQueue();
164
168
  } catch (e) {
165
169
  dispatch({
@@ -1,11 +0,0 @@
1
- import {ActionType, ContentStateInterface} from '../Contexts/RtcContext';
2
-
3
- export default function Spotlight(
4
- state: ContentStateInterface,
5
- action: ActionType<'Spotlight'>,
6
- ) {
7
- return {
8
- ...state,
9
- spotlightUid: action?.value && action.value?.length ? action.value[0] : 0,
10
- };
11
- }
@@ -1,11 +0,0 @@
1
- import {ActionType, ContentStateInterface} from '../Contexts/RtcContext';
2
-
3
- export default function UserBanned(
4
- state: ContentStateInterface,
5
- action: ActionType<'UserBanned'>,
6
- ) {
7
- return {
8
- ...state,
9
- isUserBaned: action?.value[0] || false,
10
- };
11
- }