agora-appbuilder-core 4.0.20-beta.4 → 4.0.20-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.20-beta.4",
3
+ "version": "4.0.20-beta.6",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -108,6 +108,10 @@ export type EndCallHookType = () => (
108
108
  history: History,
109
109
  ) => Promise<void>;
110
110
 
111
+ export interface AppConfig {
112
+ defaultRootFallback?: React.ComponentType;
113
+ }
114
+
111
115
  export interface CustomizationApiInterface {
112
116
  /**
113
117
  * components used to replace whole screen or subcomponents
@@ -118,6 +122,10 @@ export interface CustomizationApiInterface {
118
122
  */
119
123
  // commented for v1 release
120
124
  customRoutes?: CustomRoutesInterface[];
125
+ /**
126
+ * app config
127
+ */
128
+ config?: AppConfig;
121
129
  /**
122
130
  * Internationlization
123
131
  */
@@ -22,9 +22,17 @@ import {CUSTOM_ROUTES_PREFIX, CustomRoutesInterface} from 'customization-api';
22
22
  import PrivateRoute from './components/PrivateRoute';
23
23
  import RecordingBotRoute from './components/recording-bot/RecordingBotRoute';
24
24
  import {useIsRecordingBot} from './subComponents/recording/useIsRecordingBot';
25
+ import {LogSource, logger} from './logger/AppBuilderLogger';
26
+ import {isValidReactComponent} from './utils/common';
25
27
 
26
28
  function VideoCallWrapper(props) {
27
29
  const {isRecordingBotRoute} = useIsRecordingBot();
30
+ logger.debug(
31
+ LogSource.Internals,
32
+ 'RECORDING',
33
+ 'Check if this is a recording bot route',
34
+ isRecordingBotRoute,
35
+ );
28
36
  return isRecordingBotRoute ? (
29
37
  <RecordingBotRoute history={props.history}>
30
38
  <VideoCall />
@@ -38,6 +46,8 @@ function VideoCallWrapper(props) {
38
46
 
39
47
  function AppRoutes() {
40
48
  const CustomRoutes = useCustomization(data => data?.customRoutes);
49
+ const AppConfig = useCustomization(data => data?.config);
50
+ const {defaultRootFallback: DefaultRootFallback} = AppConfig || {};
41
51
  const RenderCustomRoutes = () => {
42
52
  try {
43
53
  return (
@@ -68,7 +78,14 @@ function AppRoutes() {
68
78
  return (
69
79
  <Switch>
70
80
  <Route exact path={'/'}>
71
- <Redirect to={'/create'} />
81
+ {DefaultRootFallback &&
82
+ (typeof DefaultRootFallback === 'object' ||
83
+ typeof DefaultRootFallback === 'function') &&
84
+ isValidReactComponent(DefaultRootFallback) ? (
85
+ <DefaultRootFallback />
86
+ ) : (
87
+ <Redirect to={'/create'} />
88
+ )}
72
89
  </Route>
73
90
  <Route exact path={'/authorize/:token?'}>
74
91
  <IDPAuth />
@@ -493,11 +493,6 @@ const AuthProvider = (props: AuthProviderProps) => {
493
493
  );
494
494
  throw new Error('Token not received');
495
495
  } else {
496
- isWeb() &&
497
- sessionStorage.setItem(
498
- 'user_id',
499
- JSON.parse(atob(response.token?.split('.')[1]))?.user_id,
500
- );
501
496
  enableTokenAuth(response.token)
502
497
  .then(() => {
503
498
  //set auth enabled on useEffect
@@ -24,12 +24,6 @@ export const getPlatformId = (): string => {
24
24
  return platformID;
25
25
  };
26
26
 
27
- export const getUserId = (): string => {
28
- if (!isWeb()) return null;
29
- const userId = sessionStorage.getItem('user_id');
30
- return userId;
31
- };
32
-
33
27
  export const getRequestHeaders = () => {
34
28
  return {
35
29
  'X-Project-ID': $config.PROJECT_ID,
@@ -53,9 +47,7 @@ export const getOriginURL = () => {
53
47
  };
54
48
 
55
49
  export const GET_UNAUTH_FLOW_API_ENDPOINT = () => {
56
- const userId = getUserId();
57
- const userIdParam = userId ? `&user_id=${userId}` : '';
58
50
  return `${$config.BACKEND_ENDPOINT}/v1/login?project_id=${
59
51
  $config.PROJECT_ID
60
- }&platform_id=${getPlatformId()}${userIdParam}`;
52
+ }&platform_id=${getPlatformId()}`;
61
53
  };
@@ -430,7 +430,9 @@ export const ParticipantCountToolbarItem = () => {
430
430
  );
431
431
  };
432
432
  export const RecordingStatusToolbarItem = () => {
433
- const recordingLabel = useString(videoRoomRecordingText)();
433
+ const recordingLabel = useString(videoRoomRecordingText)(
434
+ $config.RECORDING_MODE,
435
+ );
434
436
  const {isRecordingActive} = useRecording();
435
437
  return isRecordingActive ? (
436
438
  <ToolbarItem>
@@ -65,7 +65,7 @@ const ChatConfigure = ({children}) => {
65
65
  const chatClient = ChatClient.getInstance();
66
66
  const chatManager = chatClient.chatManager;
67
67
 
68
- const localUid = data?.uid?.toString();
68
+ const localUid = data?.channel + '_' + data?.uid?.toString();
69
69
  const agoraToken = data?.chat?.user_token;
70
70
  const {store} = React.useContext(StorageContext);
71
71
  const {
@@ -119,6 +119,7 @@ const ChatConfigure = ({children}) => {
119
119
  messages[0].chatType === ChatMessageChatType.PeerChat;
120
120
  const {msgId, from, body, localTime} = messages[0];
121
121
  const chatType = body.type;
122
+ const fromUser = from.split('_')[1];
122
123
  const {file_ext, file_name, file_url, from_platform} = messages[0]
123
124
  .attributes as ChatMessageAttributes;
124
125
 
@@ -129,8 +130,8 @@ const ChatConfigure = ({children}) => {
129
130
  //@ts-ignore
130
131
  const chatContent = body.content;
131
132
  if (isGroupChat) {
132
- showMessageNotification(chatContent, from, false);
133
- addMessageToStore(Number(from), {
133
+ showMessageNotification(chatContent, fromUser, false);
134
+ addMessageToStore(Number(fromUser), {
134
135
  msg: chatContent.replace(/^(\n)+|(\n)+$/g, ''),
135
136
  createdTimestamp: localTime,
136
137
  msgId: msgId,
@@ -139,9 +140,9 @@ const ChatConfigure = ({children}) => {
139
140
  });
140
141
  }
141
142
  if (isPeerChat) {
142
- showMessageNotification(chatContent, from, true);
143
+ showMessageNotification(chatContent, fromUser, true);
143
144
  addMessageToPrivateStore(
144
- Number(from),
145
+ Number(fromUser),
145
146
  {
146
147
  msg: chatContent.replace(/^(\n)+|(\n)+$/g, ''),
147
148
  createdTimestamp: localTime,
@@ -165,11 +166,11 @@ const ChatConfigure = ({children}) => {
165
166
  if (isGroupChat) {
166
167
  showMessageNotification(
167
168
  file_name,
168
- from,
169
+ fromUser,
169
170
  false,
170
171
  ChatMessageType.IMAGE,
171
172
  );
172
- addMessageToStore(Number(from), {
173
+ addMessageToStore(Number(fromUser), {
173
174
  msg: '',
174
175
  createdTimestamp: localTime,
175
176
  msgId: msgId,
@@ -183,12 +184,12 @@ const ChatConfigure = ({children}) => {
183
184
  if (isPeerChat) {
184
185
  showMessageNotification(
185
186
  'You got private image msg',
186
- from,
187
+ fromUser,
187
188
  true,
188
189
  ChatMessageType.IMAGE,
189
190
  );
190
191
  addMessageToPrivateStore(
191
- Number(from),
192
+ Number(fromUser),
192
193
  {
193
194
  msg: '',
194
195
  createdTimestamp: localTime,
@@ -210,11 +211,11 @@ const ChatConfigure = ({children}) => {
210
211
  if (isGroupChat) {
211
212
  showMessageNotification(
212
213
  file_name,
213
- from,
214
+ fromUser,
214
215
  false,
215
216
  ChatMessageType.FILE,
216
217
  );
217
- addMessageToStore(Number(from), {
218
+ addMessageToStore(Number(fromUser), {
218
219
  msg: '',
219
220
  createdTimestamp: localTime,
220
221
  msgId: msgId,
@@ -228,12 +229,12 @@ const ChatConfigure = ({children}) => {
228
229
  if (isPeerChat) {
229
230
  showMessageNotification(
230
231
  file_name,
231
- from,
232
+ fromUser,
232
233
  true,
233
234
  ChatMessageType.FILE,
234
235
  );
235
236
  addMessageToPrivateStore(
236
- Number(from),
237
+ Number(fromUser),
237
238
  {
238
239
  msg: '',
239
240
  createdTimestamp: localTime,
@@ -380,9 +381,13 @@ const ChatConfigure = ({children}) => {
380
381
 
381
382
  // this is local user messages
382
383
  if (option.chatType === SDKChatType.SINGLE_CHAT) {
383
- addMessageToPrivateStore(Number(option.to), messageData, true);
384
+ addMessageToPrivateStore(
385
+ Number(option.to.split('_')[1]),
386
+ messageData,
387
+ true,
388
+ );
384
389
  } else {
385
- addMessageToStore(Number(option.from), messageData);
390
+ addMessageToStore(Number(option.from.split('_')[1]), messageData);
386
391
  }
387
392
  }
388
393
  })
@@ -84,7 +84,7 @@ const ChatConfigure = ({children}) => {
84
84
  });
85
85
  // Logs into Agora Chat.
86
86
  const result = await newConn.open({
87
- user: data.uid.toString(),
87
+ user: data?.channel + '_' + data?.uid?.toString(),
88
88
  agoraToken: data.chat.user_token,
89
89
  });
90
90
  logger.debug(
@@ -105,15 +105,18 @@ const ChatConfigure = ({children}) => {
105
105
  message.ext?.from_platform === 'native'
106
106
  ? message.url
107
107
  : message.ext.file_url;
108
+
109
+ const fromUser = message?.from.split('_')[1];
110
+
108
111
  if (message.chatType === SDKChatType.GROUP_CHAT) {
109
112
  showMessageNotification(
110
113
  message.ext.file_name,
111
- message.from,
114
+ fromUser,
112
115
  false,
113
116
  message.type,
114
117
  );
115
118
 
116
- addMessageToStore(Number(message.from), {
119
+ addMessageToStore(Number(fromUser), {
117
120
  msg: '',
118
121
  createdTimestamp: message.time,
119
122
  msgId: message.id,
@@ -127,12 +130,12 @@ const ChatConfigure = ({children}) => {
127
130
  if (message.chatType === SDKChatType.SINGLE_CHAT) {
128
131
  showMessageNotification(
129
132
  message.ext.file_name,
130
- message.from,
133
+ fromUser,
131
134
  true,
132
135
  message.type,
133
136
  );
134
137
  addMessageToPrivateStore(
135
- Number(message.from),
138
+ Number(fromUser),
136
139
  {
137
140
  msg: '',
138
141
  createdTimestamp: message.time,
@@ -153,14 +156,16 @@ const ChatConfigure = ({children}) => {
153
156
  ? message.url
154
157
  : message.ext.file_url;
155
158
 
159
+ const fromUser = message?.from.split('_')[1];
160
+
156
161
  if (message.chatType === SDKChatType.GROUP_CHAT) {
157
162
  showMessageNotification(
158
163
  message.ext.file_name,
159
- message.from,
164
+ fromUser,
160
165
  false,
161
166
  message.type,
162
167
  );
163
- addMessageToStore(Number(message.from), {
168
+ addMessageToStore(Number(fromUser), {
164
169
  msg: '',
165
170
  createdTimestamp: message.time,
166
171
  msgId: message.id,
@@ -175,13 +180,13 @@ const ChatConfigure = ({children}) => {
175
180
  // show to notifcation- privat msg received
176
181
  showMessageNotification(
177
182
  message.ext.file_name,
178
- message.from,
183
+ fromUser,
179
184
  true,
180
185
  message.type,
181
186
  );
182
187
  // this is remote user messages
183
188
  addMessageToPrivateStore(
184
- Number(message.from),
189
+ Number(fromUser),
185
190
  {
186
191
  msg: '',
187
192
  createdTimestamp: message.time,
@@ -205,15 +210,17 @@ const ChatConfigure = ({children}) => {
205
210
  defaultContentRef.current[message.from]?.name,
206
211
  );
207
212
 
213
+ const fromUser = message?.from.split('_')[1];
214
+
208
215
  if (message.chatType === SDKChatType.GROUP_CHAT) {
209
216
  // show to notifcation- group msg received
210
217
  showMessageNotification(
211
218
  message.msg,
212
- message.from,
219
+ fromUser,
213
220
  false,
214
221
  message.type,
215
222
  );
216
- addMessageToStore(Number(message.from), {
223
+ addMessageToStore(Number(fromUser), {
217
224
  msg: message.msg.replace(/^(\n)+|(\n)+$/g, ''),
218
225
  createdTimestamp: message.time,
219
226
  msgId: message.id,
@@ -226,13 +233,13 @@ const ChatConfigure = ({children}) => {
226
233
  // show to notifcation- privat msg received
227
234
  showMessageNotification(
228
235
  message.msg,
229
- message.from,
236
+ fromUser,
230
237
  true,
231
238
  message.type,
232
239
  );
233
240
  // this is remote user messages
234
241
  addMessageToPrivateStore(
235
- Number(message.from),
242
+ Number(fromUser),
236
243
  {
237
244
  msg: message.msg.replace(/^(\n)+|(\n)+$/g, ''),
238
245
  createdTimestamp: message.time,
@@ -302,11 +309,13 @@ const ChatConfigure = ({children}) => {
302
309
  connRef.current
303
310
  .send(msg)
304
311
  .then(res => {
305
- console.log(
306
- '%cChatSDK: Send msg success: %s',
307
- 'color: blue',
308
- JSON.stringify(res, null, 2),
312
+ logger.debug(
313
+ LogSource.Internals,
314
+ 'CHAT',
315
+ 'Succesfully sent chat message',
316
+ option,
309
317
  );
318
+
310
319
  // update local messagre store
311
320
  const messageData = {
312
321
  msg: option.msg.replace(/^(\n)+|(\n)+$/g, ''),
@@ -322,9 +331,13 @@ const ChatConfigure = ({children}) => {
322
331
  //todo chattype as per natue type
323
332
  // this is local user messages
324
333
  if (option.chatType === SDKChatType.SINGLE_CHAT) {
325
- addMessageToPrivateStore(Number(option.to), messageData, true);
334
+ addMessageToPrivateStore(
335
+ Number(option?.to.split('_')[1]),
336
+ messageData,
337
+ true,
338
+ );
326
339
  } else {
327
- addMessageToStore(Number(option.from), messageData);
340
+ addMessageToStore(Number(option?.from.split('_')[1]), messageData);
328
341
  }
329
342
  })
330
343
  .catch(error => {
@@ -358,7 +371,6 @@ const ChatConfigure = ({children}) => {
358
371
  },
359
372
  );
360
373
  const res = await response.json();
361
- sessionStorage.removeItem('user_id');
362
374
  logger.debug(
363
375
  LogSource.Internals,
364
376
  'CHAT',
@@ -14,6 +14,7 @@ import type {RouteProps} from 'react-router';
14
14
  import StorageContext from '../StorageContext';
15
15
  import Loading from '../../subComponents/Loading';
16
16
  import {useIsRecordingBot} from '../../subComponents/recording/useIsRecordingBot';
17
+ import {LogSource, logger} from '../../logger/AppBuilderLogger';
17
18
 
18
19
  interface RecordingBotRouteProps extends RouteProps {
19
20
  children: React.ReactNode;
@@ -25,6 +26,11 @@ const RecordingBotRoute: React.FC<RecordingBotRouteProps> = props => {
25
26
  const [ready, setReady] = useState(false);
26
27
  const {recordingBotToken} = useIsRecordingBot();
27
28
  useEffect(() => {
29
+ logger.debug(
30
+ LogSource.Internals,
31
+ 'RECORDING',
32
+ 'Inside the bot route wrapper',
33
+ );
28
34
  setStore &&
29
35
  setStore(prevState => ({
30
36
  ...prevState,
@@ -1254,7 +1254,9 @@ export const VideoCallScreenLabels: I18nVideoCallScreenLabelsInterface = {
1254
1254
  [waitingRoomApprovalRejectionToastSubHeading]:
1255
1255
  'Permission to enter the meeting was denied by the host',
1256
1256
 
1257
- [videoRoomRecordingText]: 'Recording',
1257
+ [videoRoomRecordingText]: mode =>
1258
+ mode === 'MIX' ? 'Cloud recording' : 'Web recording',
1259
+
1258
1260
  [videoRoomGoToActiveSpeakerText]: 'Go To Active Speaker',
1259
1261
  [videoRoomScreenshareText]: username => `${username}'s screenshare`,
1260
1262
  [videoRoomStartingCallText]: 'Starting Call. Just a second.',
@@ -16,9 +16,9 @@ export const initTransportLayerForAgora = () => {
16
16
 
17
17
  const getConfig = () => {
18
18
  const loggerConfig = new DatadogProviderConfiguration(
19
- 'pubdb41371dc4efb3e57a918a4f2096578c',
19
+ 'pub5b520008af91c772f322121cb2adc81c',
20
20
  'none',
21
- 'fc33ae27-6fd6-4c7b-94c6-c2c11b9f565e',
21
+ 'fc33ae27-6fd6-4c7b-94c6-c2c11b9f565e', // application ID
22
22
  true, // track User interactions (e.g.: Tap on buttons. You can use 'accessibilityLabel' element property to give tap action the name, otherwise element type will be reported)
23
23
  true, // track XHR Resources
24
24
  true, // track Errors
@@ -32,7 +32,7 @@ const getConfig = () => {
32
32
  // Optional: sample RUM sessions (here, 100% of session will be sent to Datadog. Default = 100%)
33
33
  loggerConfig.sampleRate = 100;
34
34
  // Optional: The service name for your application
35
- loggerConfig.serviceName = 'app-builder-core-frontend-native';
35
+ loggerConfig.serviceName = 'app-builder-core-frontend-nativev2';
36
36
 
37
37
  if (__DEV__) {
38
38
  // Optional: Send data more frequently
@@ -1,7 +1,7 @@
1
1
  import {StatusType, datadogLogs} from '@datadog/browser-logs';
2
2
  import {version as cli_version} from '../../../../package.json';
3
3
 
4
- const DATADOG_CLIENT_TOKEN = 'pubeccdaed5357d217e2c75e85aaef432fe';
4
+ const DATADOG_CLIENT_TOKEN = 'pubad10d7feb87f0b039c267e69b46ee84e';
5
5
  const DATADOG_SITE = 'datadoghq.com';
6
6
 
7
7
  export const initTransportLayerForAgora = () => {
@@ -10,7 +10,7 @@ export const initTransportLayerForAgora = () => {
10
10
  site: DATADOG_SITE,
11
11
  forwardErrorsToLogs: false,
12
12
  sessionSampleRate: 100,
13
- service: 'app-builder-core-frontend',
13
+ service: 'app-builder-core-frontendv2',
14
14
  version: cli_version,
15
15
  env: 'none',
16
16
  });
@@ -1,18 +1,9 @@
1
1
  /** ***** EVENTS ACTIONS BEGINS***** */
2
- // 1. RECORDING
3
- const RECORDING_STARTED = 'RECORDING_STARTED';
4
- const RECORDING_STOPPED = 'RECORDING_STOPPED';
5
- const RECORDING_STARTED_BY = 'RECORDING_STARTED_BY';
6
- const REQUEST_TO_STOP_RECORDING = 'REQUEST_TO_STOP_RECORDING';
7
- // 2. SCREENSHARE
2
+ // 1. SCREENSHARE
8
3
  const SCREENSHARE_STARTED = 'SCREENSHARE_STARTED';
9
4
  const SCREENSHARE_STOPPED = 'SCREENSHARE_STOPPED';
10
5
 
11
6
  const EventActions = {
12
- RECORDING_STARTED_BY,
13
- RECORDING_STARTED,
14
- RECORDING_STOPPED,
15
- REQUEST_TO_STOP_RECORDING,
16
7
  SCREENSHARE_STARTED,
17
8
  SCREENSHARE_STOPPED,
18
9
  };
@@ -21,7 +12,8 @@ const EventActions = {
21
12
 
22
13
  /** ***** EVENT NAMES BEGINS ***** */
23
14
  // 1. RECORDING
24
- const RECORDING_ATTRIBUTE = 'recording';
15
+ const RECORDING_STATE_ATTRIBUTE = 'recording_state';
16
+ const RECORDING_STARTED_BY_ATTRIBUTE = 'recording_started_by';
25
17
  // 2. SCREENSHARE
26
18
  const SCREENSHARE_ATTRIBUTE = 'screenshare';
27
19
  // 2. LIVE STREAMING
@@ -48,7 +40,8 @@ const BOARD_COLOR_CHANGED = 'BOARD_COLOR_CHANGED';
48
40
  const WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION = 'WHITEBOARD_L_I_U_P';
49
41
 
50
42
  const EventNames = {
51
- RECORDING_ATTRIBUTE,
43
+ RECORDING_STATE_ATTRIBUTE,
44
+ RECORDING_STARTED_BY_ATTRIBUTE,
52
45
  RAISED_ATTRIBUTE,
53
46
  ROLE_ATTRIBUTE,
54
47
  PUBLIC_CHAT_MESSAGE,
@@ -150,8 +150,10 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
150
150
  ? SDKChatType.SINGLE_CHAT
151
151
  : SDKChatType.GROUP_CHAT,
152
152
  type: ChatMessageType.TXT,
153
- from: data.uid.toString(),
154
- to: privateChatUser ? privateChatUser.toString() : groupID,
153
+ from: data.channel + '_' + data.uid.toString(),
154
+ to: privateChatUser
155
+ ? data.channel + '_' + privateChatUser.toString()
156
+ : groupID,
155
157
  msg: message,
156
158
  };
157
159
  sendChatSDKMessage(option);
@@ -145,13 +145,17 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
145
145
  return;
146
146
  }
147
147
 
148
+ debugger;
149
+ console.log(data);
148
150
  const option = {
149
151
  chatType: privateChatUser
150
152
  ? SDKChatType.SINGLE_CHAT
151
153
  : SDKChatType.GROUP_CHAT,
152
154
  type: ChatMessageType.TXT,
153
- from: data.uid.toString(),
154
- to: privateChatUser ? privateChatUser.toString() : groupID,
155
+ from: data.channel + '_' + data.uid.toString(),
156
+ to: privateChatUser
157
+ ? data.channel + '_' + privateChatUser.toString()
158
+ : groupID,
155
159
  msg: message,
156
160
  };
157
161
  sendChatSDKMessage(option);
@@ -62,7 +62,7 @@ export const ChatActionMenu = (props: CaptionsActionMenuProps) => {
62
62
  const {defaultContent} = useContent();
63
63
 
64
64
  const {
65
- data: {isHost, chat},
65
+ data: {isHost, chat, channel},
66
66
  } = useRoomInfo();
67
67
 
68
68
  const groupID = chat.group_id;
@@ -167,7 +167,13 @@ export const ChatActionMenu = (props: CaptionsActionMenuProps) => {
167
167
  confirmLabel={confirmLabel}
168
168
  confirmLabelStyle={{color: $config.SEMANTIC_ERROR}}
169
169
  onConfirmClick={() => {
170
- deleteAttachment(msgId, recallFromUser.toString(), chatType);
170
+ deleteAttachment(
171
+ msgId,
172
+ privateChatUser
173
+ ? channel + '_' + recallFromUser
174
+ : recallFromUser.toString(),
175
+ chatType,
176
+ );
171
177
  if (chatType === SDKChatType.SINGLE_CHAT) {
172
178
  removeMessageFromPrivateStore(msgId, isLocal);
173
179
  }
@@ -128,11 +128,13 @@ export const ChatAttachmentButton = (props: ChatAttachmentButtonProps) => {
128
128
  const option = {
129
129
  type: isImageUploaded ? ChatMessageType.IMAGE : ChatMessageType.FILE,
130
130
  url: filePath,
131
- to: privateChatUser ? privateChatUser.toString() : groupID,
131
+ to: privateChatUser
132
+ ? data.channel + '_' + privateChatUser.toString()
133
+ : groupID,
132
134
  chatType: privateChatUser
133
135
  ? SDKChatType.SINGLE_CHAT
134
136
  : SDKChatType.GROUP_CHAT,
135
- from: data.uid.toString(),
137
+ from: data.channel + '_' + data.uid.toString(),
136
138
  fileName: result[0].name,
137
139
  ext: {
138
140
  file_length: result[0].size,
@@ -171,9 +173,13 @@ export const ChatAttachmentButton = (props: ChatAttachmentButtonProps) => {
171
173
 
172
174
  // this is local user messages
173
175
  if (message.chatType === ChatMessageChatType.PeerChat) {
174
- addMessageToPrivateStore(Number(message.to), messageData, true);
176
+ addMessageToPrivateStore(
177
+ Number(message.to.split('_')[1]),
178
+ messageData,
179
+ true,
180
+ );
175
181
  } else {
176
- addMessageToStore(Number(message.from), messageData);
182
+ addMessageToStore(Number(message.from.split('_')[1]), messageData);
177
183
  }
178
184
 
179
185
  setUploadStatus(UploadStatus.SUCCESS);
@@ -80,8 +80,10 @@ const ChatSendButton = (props: ChatSendButtonProps) => {
80
80
  : SDKChatType.GROUP_CHAT,
81
81
  type: msgType as ChatMessageType,
82
82
  msg: msgType === ChatMessageType.TXT ? message : '', // currenlt not supporting combinarion msg (file+txt)
83
- from: data.uid.toString(),
84
- to: selectedUserId ? selectedUserId.toString() : groupID,
83
+ from: data.channel + '_' + data.uid.toString(),
84
+ to: selectedUserId
85
+ ? data.channel + '_' + selectedUserId.toString()
86
+ : groupID,
85
87
  ext: {
86
88
  file_length,
87
89
  file_ext,
@@ -61,7 +61,7 @@ const ImagePopup = (props: ImagePopupProps) => {
61
61
  React.useState(false);
62
62
  const {privateChatUser} = useChatUIControls();
63
63
  const {
64
- data: {isHost, chat},
64
+ data: {isHost, chat, channel},
65
65
  } = useRoomInfo();
66
66
  const {downloadAttachment, deleteAttachment} = useChatConfigure();
67
67
  const {removeMessageFromPrivateStore, removeMessageFromStore} =
@@ -284,7 +284,13 @@ const ImagePopup = (props: ImagePopupProps) => {
284
284
  confirmLabel={confirmLabel}
285
285
  confirmLabelStyle={{color: $config.SEMANTIC_ERROR}}
286
286
  onConfirmClick={() => {
287
- deleteAttachment(msgId, recallFromUser.toString(), chatType);
287
+ deleteAttachment(
288
+ msgId,
289
+ privateChatUser
290
+ ? channel + '_' + recallFromUser
291
+ : recallFromUser.toString(),
292
+ chatType,
293
+ );
288
294
  if (chatType === SDKChatType.SINGLE_CHAT) {
289
295
  removeMessageFromPrivateStore(msgId, isLocal);
290
296
  }
@@ -23,7 +23,7 @@ import {createHook} from 'customization-implementation';
23
23
  import {useString} from '../../utils/useString';
24
24
  import ChatContext from '../../components/ChatContext';
25
25
  import events, {PersistanceLevel} from '../../rtm-events-api';
26
- import {EventActions, EventNames} from '../../rtm-events';
26
+ import {EventNames} from '../../rtm-events';
27
27
  import {useContent} from 'customization-api';
28
28
  import {trimText} from '../../utils/common';
29
29
  import {useRoomInfo} from 'customization-api';
@@ -109,6 +109,17 @@ interface RecordingProviderProps {
109
109
  * Sends a control message to all users in the channel over RTM to indicate that
110
110
  * Cloud recording has started/stopped.
111
111
  */
112
+ enum RECORDING_REQUEST_STATE {
113
+ PENDING = 'PENDING',
114
+ STARTED_MIX = 'STARTED_MIX',
115
+ STARTED_WEB = 'STARTED_WEB',
116
+ STOPPED = 'STOPPED',
117
+ FAILED = 'FAILED',
118
+ }
119
+ const RecordingActions = {
120
+ RECORDING_REQUEST_STATE: RECORDING_REQUEST_STATE,
121
+ REQUEST_TO_STOP_RECORDING: 'REQUEST_TO_STOP_RECORDING',
122
+ };
112
123
 
113
124
  const recordingMode = $config.RECORDING_MODE || 'MIX';
114
125
 
@@ -193,10 +204,18 @@ const RecordingProvider = (props: RecordingProviderProps) => {
193
204
  leadingIcon: null,
194
205
  });
195
206
  }
196
- }, [isRecordingActive, callActive, isHost]);
207
+ }, [isRecordingActive, callActive, isHost, uidWhoStarted]);
197
208
 
198
209
  const startRecording = () => {
199
210
  log('start recording API called');
211
+ if (inProgress) {
212
+ logger.error(
213
+ LogSource.Internals,
214
+ 'RECORDING',
215
+ 'start recording already in progress. Aborting..',
216
+ );
217
+ return;
218
+ }
200
219
  logger.log(
201
220
  LogSource.NetworkRest,
202
221
  'recording_start',
@@ -208,12 +227,6 @@ const RecordingProvider = (props: RecordingProviderProps) => {
208
227
  let recordinghostURL = getOriginURL();
209
228
  // let recordinghostURL =
210
229
  // 'https://app-builder-core-git-hotfix-recording-bot-ends-r-253634-agoraio.vercel.app';
211
- if (inProgress) {
212
- console.error(
213
- 'web-recording - start recording API already in progress',
214
- );
215
- return;
216
- }
217
230
  if (recordinghostURL.includes('localhost')) {
218
231
  console.error(
219
232
  'web-recording - Recording url cannot be localhost. It should be a valid deployed URL',
@@ -224,6 +237,14 @@ const RecordingProvider = (props: RecordingProviderProps) => {
224
237
  url = `${recordinghostURL}/${passphrase}`;
225
238
  }
226
239
  setInProgress(true);
240
+ events.send(
241
+ EventNames.RECORDING_STATE_ATTRIBUTE,
242
+ JSON.stringify({
243
+ action: RecordingActions.RECORDING_REQUEST_STATE.PENDING,
244
+ value: `${localUid}`,
245
+ }),
246
+ PersistanceLevel.Session,
247
+ );
227
248
  logger.debug(
228
249
  LogSource.NetworkRest,
229
250
  'recording_start',
@@ -256,24 +277,28 @@ const RecordingProvider = (props: RecordingProviderProps) => {
256
277
  'start recording successfully',
257
278
  res,
258
279
  );
259
- /**
260
- * 1. Once the backend sucessfuly starts recording, send message
261
- * in the channel indicating that cloud/web recording is now active.
262
- */
280
+ // 1. set the local recording state to true to update the UI
263
281
  events.send(
264
- EventNames.RECORDING_ATTRIBUTE,
282
+ EventNames.RECORDING_STARTED_BY_ATTRIBUTE,
265
283
  JSON.stringify({
266
- action: EventActions.RECORDING_STARTED_BY,
284
+ action: '',
267
285
  value: `${localUid}`,
268
286
  }),
269
287
  PersistanceLevel.Session,
270
288
  );
271
- // 2. set the local recording state to true to update the UI
272
289
  setUidWhoStarted(localUid);
273
- // 3. Check if recording mode is cloud
290
+ // 2. Check if recording mode is cloud
274
291
  if (recordingMode === 'MIX') {
275
292
  setInProgress(false);
276
293
  setRecordingActive(true);
294
+ events.send(
295
+ EventNames.RECORDING_STATE_ATTRIBUTE,
296
+ JSON.stringify({
297
+ action: RecordingActions.RECORDING_REQUEST_STATE.STARTED_MIX,
298
+ value: `${localUid}`,
299
+ }),
300
+ PersistanceLevel.Session,
301
+ );
277
302
  // 3.a Set the presenter mode if screen share is active
278
303
  // 3.b Get the most recent screenshare uid
279
304
  const sorted = Object.entries(screenShareData)
@@ -292,21 +317,30 @@ const RecordingProvider = (props: RecordingProviderProps) => {
292
317
  }
293
318
  }
294
319
  } else if (res.status === 500) {
295
- setInProgress(false);
296
320
  showErrorToast(headingStartError, subheadingError);
321
+ throw Error(`Internal server error ${res.status}`);
297
322
  } else {
298
- setInProgress(false);
299
323
  showErrorToast(headingStartError);
324
+ throw Error(`Internal server error ${res.status}`);
300
325
  }
301
326
  })
302
327
  .catch(err => {
328
+ setRecordingActive(false);
329
+ setInProgress(false);
330
+ events.send(
331
+ EventNames.RECORDING_STATE_ATTRIBUTE,
332
+ JSON.stringify({
333
+ action: RecordingActions.RECORDING_REQUEST_STATE.FAILED,
334
+ value: `${localUid}`,
335
+ }),
336
+ PersistanceLevel.Session,
337
+ );
303
338
  logger.error(
304
339
  LogSource.NetworkRest,
305
340
  'recording_start',
306
341
  'Error while start recording',
307
342
  err,
308
343
  );
309
- setInProgress(false);
310
344
  });
311
345
  };
312
346
 
@@ -315,15 +349,6 @@ const RecordingProvider = (props: RecordingProviderProps) => {
315
349
  * Any host in the channel can stop recording.
316
350
  */
317
351
  logger.debug(LogSource.Internals, 'RECORDING', 'stop recording API called');
318
- if (inProgress) {
319
- logger.error(
320
- LogSource.Internals,
321
- 'RECORDING',
322
- 'stop recording already in progress. Aborting..',
323
- );
324
- return;
325
- }
326
- setInProgress(true);
327
352
  fetchRetry(`${$config.BACKEND_ENDPOINT}/v1/recording/stop`, {
328
353
  method: 'POST',
329
354
  headers: {
@@ -336,7 +361,6 @@ const RecordingProvider = (props: RecordingProviderProps) => {
336
361
  }),
337
362
  })
338
363
  .then(res => {
339
- // Bot when stops the recording, it does not reach here
340
364
  setInProgress(false);
341
365
  if (res.status === 200 || res.status === 202) {
342
366
  logger.debug(
@@ -351,10 +375,10 @@ const RecordingProvider = (props: RecordingProviderProps) => {
351
375
  */
352
376
  log('Recording-bot: recording stopped successfully');
353
377
  events.send(
354
- EventNames.RECORDING_ATTRIBUTE,
378
+ EventNames.RECORDING_STATE_ATTRIBUTE,
355
379
  JSON.stringify({
356
- action: EventActions.RECORDING_STOPPED,
357
- value: 'success',
380
+ action: RecordingActions.RECORDING_REQUEST_STATE.STOPPED,
381
+ value: `${localUid}`,
358
382
  }),
359
383
  PersistanceLevel.Session,
360
384
  );
@@ -386,42 +410,50 @@ const RecordingProvider = (props: RecordingProviderProps) => {
386
410
  setInProgress(false);
387
411
  log('stop recording', err);
388
412
  events.send(
389
- EventNames.RECORDING_ATTRIBUTE,
413
+ EventNames.RECORDING_STATE_ATTRIBUTE,
390
414
  JSON.stringify({
391
- action: EventActions.RECORDING_STOPPED,
392
- value: 'error',
415
+ action: RecordingActions.RECORDING_REQUEST_STATE.FAILED,
416
+ value: `${localUid}`,
393
417
  }),
394
418
  PersistanceLevel.Session,
395
419
  );
396
420
  });
397
421
  }, [
398
422
  headingStopError,
399
- inProgress,
400
423
  roomId.host,
401
424
  setRecordingActive,
402
425
  store.token,
403
426
  subheadingError,
427
+ localUid,
404
428
  ]);
405
429
 
406
430
  const stopRecording = useCallback(() => {
431
+ setInProgress(true);
432
+ events.send(
433
+ EventNames.RECORDING_STATE_ATTRIBUTE,
434
+ JSON.stringify({
435
+ action: RecordingActions.RECORDING_REQUEST_STATE.PENDING,
436
+ value: `${localUid}`,
437
+ }),
438
+ PersistanceLevel.Session,
439
+ );
407
440
  if (recordingMode === 'WEB') {
408
441
  log('Stopping recording by sending event to bot');
409
442
  // send stop request to bot
410
- setInProgress(true);
411
443
  events.send(
412
- EventNames.RECORDING_ATTRIBUTE,
444
+ EventNames.RECORDING_STATE_ATTRIBUTE,
413
445
  JSON.stringify({
414
- action: EventActions.REQUEST_TO_STOP_RECORDING,
415
- value: '',
446
+ action: RecordingActions.REQUEST_TO_STOP_RECORDING,
447
+ value: `${localUid}`,
416
448
  }),
417
- PersistanceLevel.Session,
449
+ PersistanceLevel.None,
418
450
  100000, // bot uid
419
451
  );
420
452
  } else {
421
453
  log('Stopping recording by calling stop');
422
454
  _stopRecording();
423
455
  }
424
- }, [_stopRecording]);
456
+ }, [_stopRecording, localUid]);
425
457
 
426
458
  const fetchRecordings = useCallback(
427
459
  (page: number) => {
@@ -483,35 +515,46 @@ const RecordingProvider = (props: RecordingProviderProps) => {
483
515
 
484
516
  // Events
485
517
  useEffect(() => {
486
- events.on(EventNames.RECORDING_ATTRIBUTE, data => {
487
- log('recording attribute received', data);
518
+ events.on(EventNames.RECORDING_STATE_ATTRIBUTE, data => {
519
+ log('recording_state attribute received', data);
488
520
  const payload = JSON.parse(data.payload);
489
521
  const action = payload.action;
490
- const value = payload.value;
491
522
  switch (action) {
492
- case EventActions.RECORDING_STARTED_BY:
493
- setUidWhoStarted(parseInt(value));
494
- if (recordingMode === 'MIX') {
495
- setRecordingActive(true);
496
- }
523
+ case RecordingActions.RECORDING_REQUEST_STATE.PENDING:
524
+ setInProgress(true);
497
525
  break;
498
- case EventActions.RECORDING_STARTED:
526
+ case RecordingActions.RECORDING_REQUEST_STATE.STARTED_MIX:
499
527
  setInProgress(false);
500
528
  setRecordingActive(true);
501
529
  break;
502
- case EventActions.RECORDING_STOPPED:
530
+ case RecordingActions.RECORDING_REQUEST_STATE.STARTED_WEB:
531
+ setInProgress(false);
532
+ setRecordingActive(true);
533
+ break;
534
+ case RecordingActions.RECORDING_REQUEST_STATE.STOPPED:
503
535
  setInProgress(false);
504
536
  setRecordingActive(false);
505
537
  break;
506
- case EventActions.REQUEST_TO_STOP_RECORDING:
538
+ /**
539
+ * The below case is not persisted, hence we need not worry about whether the
540
+ * new user gets the correct state or not
541
+ */
542
+ case RecordingActions.REQUEST_TO_STOP_RECORDING:
507
543
  _stopRecording();
508
544
  break;
509
545
  default:
510
546
  break;
511
547
  }
512
548
  });
549
+ events.on(EventNames.RECORDING_STARTED_BY_ATTRIBUTE, data => {
550
+ log('recording_started_by attribute received', data);
551
+ const payload = JSON.parse(data.payload);
552
+ const value = payload.value;
553
+ setUidWhoStarted(parseInt(value));
554
+ });
513
555
  return () => {
514
- events.off(EventNames.RECORDING_ATTRIBUTE);
556
+ events.off(EventNames.RECORDING_STATE_ATTRIBUTE);
557
+ events.off(EventNames.RECORDING_STARTED_BY_ATTRIBUTE);
515
558
  };
516
559
  }, [
517
560
  roomId.host,
@@ -600,10 +643,12 @@ const RecordingProvider = (props: RecordingProviderProps) => {
600
643
  'RECORDING',
601
644
  'Recording-bot: sending event recording-started event to users in the call',
602
645
  );
646
+ setInProgress(false);
647
+ setRecordingActive(true);
603
648
  events.send(
604
- EventNames.RECORDING_ATTRIBUTE,
649
+ EventNames.RECORDING_STATE_ATTRIBUTE,
605
650
  JSON.stringify({
606
- action: EventActions.RECORDING_STARTED,
651
+ action: RecordingActions.RECORDING_REQUEST_STATE.STARTED_WEB,
607
652
  value: `${localUid}`,
608
653
  }),
609
654
  PersistanceLevel.Session,