agora-appbuilder-core 4.1.19-beta.4 → 4.1.20
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 +1 -1
- package/template/bridge/rtc/webNg/RtcEngine.ts +320 -83
- package/template/customization-api/atoms.ts +0 -2
- package/template/customization-api/customEvents.ts +1 -4
- package/template/customization-api/index.ts +0 -1
- package/template/customization-api/typeDefinition.ts +1 -6
- package/template/defaultConfig.js +2 -2
- package/template/src/components/Controls.tsx +3 -1
- package/template/src/components/CustomSidePanel.tsx +13 -13
- package/template/src/components/RTMConfigure.tsx +0 -13
- package/template/src/components/livestream/LiveStreamContext.tsx +6 -2
- package/template/src/components/participants/UserActionMenuOptions.tsx +2 -29
- package/template/src/pages/Create.tsx +19 -31
- package/template/src/pages/video-call/ActionSheet.native.tsx +0 -2
- package/template/src/pages/video-call/ActionSheet.tsx +0 -2
- package/template/src/pages/video-call/ActionSheetHandle.tsx +0 -1
- package/template/src/pages/video-call/SidePanelHeader.tsx +1 -3
- package/template/src/pages/video-call/VideoCallScreen.tsx +0 -3
- package/template/src/pages/video-call/VideoRenderer.tsx +0 -4
- package/template/src/subComponents/LocalEndCall.tsx +1 -1
- package/template/src/subComponents/ScreenShareNotice.tsx +1 -1
- package/template/src/subComponents/SidePanelHeader.tsx +2 -16
- package/template/src/subComponents/recording/__tests__/recordingJourney.test.ts +25 -0
- package/template/src/subComponents/recording/__tests__/recordingLayoutResponse.test.ts +56 -0
- package/template/src/subComponents/recording/recordingJourney.ts +12 -0
- package/template/src/subComponents/recording/recordingLayoutResponse.ts +67 -0
- package/template/src/subComponents/recording/useRecording.tsx +28 -12
- package/template/src/subComponents/recording/useRecordingLayoutQuery.tsx +35 -45
- package/template/src/subComponents/screenshare/ScreenshareButton.tsx +1 -1
- package/template/src/subComponents/screenshare/ScreenshareConfigure.tsx +483 -31
- package/template/src/subComponents/screenshare/__tests__/screenshareJourney.test.ts +63 -0
- package/template/src/subComponents/screenshare/screenshareJourney.ts +43 -0
- package/template/src/subComponents/screenshare/useScreenshare.tsx +18 -2
- package/template/customization-api/ai-agent.ts +0 -4
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import React, {useContext, useEffect, useRef, useState} from 'react';
|
|
13
13
|
import {DispatchContext, PropsContext, UidType} from '../../../agora-rn-uikit';
|
|
14
|
-
import {ScreenshareContext} from './useScreenshare';
|
|
14
|
+
import {ScreenshareContext, ScreenshareStopOrigin} from './useScreenshare';
|
|
15
15
|
import {
|
|
16
16
|
getGridLayoutName,
|
|
17
17
|
getPinnedLayoutName,
|
|
@@ -38,6 +38,15 @@ import {
|
|
|
38
38
|
videoRoomScreenShareErrorToastSubHeading,
|
|
39
39
|
} from '../../language/default-labels/videoCallScreenLabels';
|
|
40
40
|
import {LogSource, logger} from '../../logger/AppBuilderLogger';
|
|
41
|
+
import getUniqueID from '../../utils/getUniqueID';
|
|
42
|
+
import {
|
|
43
|
+
getScreenshareSessionId,
|
|
44
|
+
getScreenshareError,
|
|
45
|
+
isUserCancelOrPermissionDenied,
|
|
46
|
+
SCREENSHARE_JOURNEY,
|
|
47
|
+
} from './screenshareJourney';
|
|
48
|
+
|
|
49
|
+
type ScreenshareAction = 'start' | 'stop';
|
|
41
50
|
|
|
42
51
|
export const ScreenshareContextConsumer = ScreenshareContext.Consumer;
|
|
43
52
|
|
|
@@ -48,6 +57,13 @@ export const ScreenshareConfigure = (props: {
|
|
|
48
57
|
const toastHeading = useString(videoRoomScreenShareErrorToastHeading)();
|
|
49
58
|
const toastSubHeading = useString(videoRoomScreenShareErrorToastSubHeading)();
|
|
50
59
|
const [isScreenshareActive, setScreenshareActive] = useState(false);
|
|
60
|
+
const activeScreenshareSessionIdRef = useRef<string | null>(null);
|
|
61
|
+
const stopScreenshareRef = useRef<
|
|
62
|
+
(
|
|
63
|
+
origin?: ScreenshareStopOrigin,
|
|
64
|
+
stopActorUid?: UidType,
|
|
65
|
+
) => Promise<void> | void
|
|
66
|
+
>(() => {});
|
|
51
67
|
const {dispatch} = useContext(DispatchContext);
|
|
52
68
|
const rtc = useRtc();
|
|
53
69
|
const {defaultContent, activeUids, pinnedUid, secondaryPinnedUid} =
|
|
@@ -157,10 +173,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
157
173
|
useEffect(() => {
|
|
158
174
|
const unsubKickScreenshare = events.on(
|
|
159
175
|
controlMessageEnum.kickScreenshare,
|
|
160
|
-
|
|
176
|
+
data => {
|
|
161
177
|
//if screenscreen already active. then below method will stop the screen share
|
|
162
|
-
|
|
163
|
-
rtc.RtcEngineUnsafe.startScreenshare();
|
|
178
|
+
stopScreenshareRef.current('remote_host_removal', data?.sender);
|
|
164
179
|
},
|
|
165
180
|
);
|
|
166
181
|
const unsubScreenshareAttribute = events.on(
|
|
@@ -232,9 +247,47 @@ export const ScreenshareConfigure = (props: {
|
|
|
232
247
|
};
|
|
233
248
|
}, []);
|
|
234
249
|
|
|
235
|
-
const ScreenshareStoppedCallback = (
|
|
250
|
+
const ScreenshareStoppedCallback = (
|
|
251
|
+
stopOrigin: ScreenshareStopOrigin = 'unknown',
|
|
252
|
+
screenshareAttemptId = getUniqueID(),
|
|
253
|
+
screenshareSessionId = activeScreenshareSessionIdRef.current ||
|
|
254
|
+
'unknown-session',
|
|
255
|
+
stopActorUid?: UidType,
|
|
256
|
+
) => {
|
|
257
|
+
const callbackStartedAt = Date.now();
|
|
258
|
+
logger.log(
|
|
259
|
+
LogSource.Internals,
|
|
260
|
+
'SCREENSHARE',
|
|
261
|
+
`${SCREENSHARE_JOURNEY} screen share stop callback received from RTC engine`,
|
|
262
|
+
{
|
|
263
|
+
action: 'stop',
|
|
264
|
+
stage: 'stop_callback',
|
|
265
|
+
outcome: 'started',
|
|
266
|
+
screenshareAttemptId,
|
|
267
|
+
screenshareSessionId,
|
|
268
|
+
recordingActive: props.isRecordingActive,
|
|
269
|
+
screenShareUid,
|
|
270
|
+
stopOrigin,
|
|
271
|
+
stopActorUid,
|
|
272
|
+
},
|
|
273
|
+
);
|
|
236
274
|
setScreenshareActive(false);
|
|
237
|
-
logger.
|
|
275
|
+
logger.log(
|
|
276
|
+
LogSource.Internals,
|
|
277
|
+
'SCREENSHARE',
|
|
278
|
+
`${SCREENSHARE_JOURNEY} screen share stop local active state set to inactive`,
|
|
279
|
+
{
|
|
280
|
+
action: 'stop',
|
|
281
|
+
stage: 'local_active_state',
|
|
282
|
+
outcome: 'success',
|
|
283
|
+
screenshareAttemptId,
|
|
284
|
+
screenshareSessionId,
|
|
285
|
+
recordingActive: props.isRecordingActive,
|
|
286
|
+
screenShareUid,
|
|
287
|
+
stopOrigin,
|
|
288
|
+
stopActorUid,
|
|
289
|
+
},
|
|
290
|
+
);
|
|
238
291
|
events.send(
|
|
239
292
|
EventNames.SCREENSHARE_ATTRIBUTE,
|
|
240
293
|
JSON.stringify({
|
|
@@ -243,6 +296,22 @@ export const ScreenshareConfigure = (props: {
|
|
|
243
296
|
}),
|
|
244
297
|
PersistanceLevel.Sender,
|
|
245
298
|
);
|
|
299
|
+
logger.log(
|
|
300
|
+
LogSource.Internals,
|
|
301
|
+
'SCREENSHARE',
|
|
302
|
+
`${SCREENSHARE_JOURNEY} screen share stop RTM event sent to remote users`,
|
|
303
|
+
{
|
|
304
|
+
action: 'stop',
|
|
305
|
+
stage: 'rtm_event',
|
|
306
|
+
outcome: 'success',
|
|
307
|
+
screenshareAttemptId,
|
|
308
|
+
screenshareSessionId,
|
|
309
|
+
recordingActive: props.isRecordingActive,
|
|
310
|
+
screenShareUid,
|
|
311
|
+
stopOrigin,
|
|
312
|
+
stopActorUid,
|
|
313
|
+
},
|
|
314
|
+
);
|
|
246
315
|
setScreenShareData(prevState => {
|
|
247
316
|
return {
|
|
248
317
|
...prevState,
|
|
@@ -253,6 +322,22 @@ export const ScreenshareConfigure = (props: {
|
|
|
253
322
|
},
|
|
254
323
|
};
|
|
255
324
|
});
|
|
325
|
+
logger.log(
|
|
326
|
+
LogSource.Internals,
|
|
327
|
+
'SCREENSHARE',
|
|
328
|
+
`${SCREENSHARE_JOURNEY} screen share stop local context set to inactive`,
|
|
329
|
+
{
|
|
330
|
+
action: 'stop',
|
|
331
|
+
stage: 'screenshare_context',
|
|
332
|
+
outcome: 'success',
|
|
333
|
+
screenshareAttemptId,
|
|
334
|
+
screenshareSessionId,
|
|
335
|
+
recordingActive: props.isRecordingActive,
|
|
336
|
+
screenShareUid,
|
|
337
|
+
stopOrigin,
|
|
338
|
+
stopActorUid,
|
|
339
|
+
},
|
|
340
|
+
);
|
|
256
341
|
//if local user stopped the screenshare then change layout to grid
|
|
257
342
|
//if user pinned somebody then don't triggerlayout change
|
|
258
343
|
if (!pinnedUidRef.current.pinnedUid) {
|
|
@@ -265,6 +350,40 @@ export const ScreenshareConfigure = (props: {
|
|
|
265
350
|
value: [0],
|
|
266
351
|
});
|
|
267
352
|
}
|
|
353
|
+
logger.log(
|
|
354
|
+
LogSource.Internals,
|
|
355
|
+
'SCREENSHARE',
|
|
356
|
+
`${SCREENSHARE_JOURNEY} screen share stop layout update completed when applicable`,
|
|
357
|
+
{
|
|
358
|
+
action: 'stop',
|
|
359
|
+
stage: 'layout_update',
|
|
360
|
+
outcome: 'success',
|
|
361
|
+
screenshareAttemptId,
|
|
362
|
+
screenshareSessionId,
|
|
363
|
+
recordingActive: props.isRecordingActive,
|
|
364
|
+
screenShareUid,
|
|
365
|
+
stopOrigin,
|
|
366
|
+
stopActorUid,
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
logger.log(
|
|
370
|
+
LogSource.Internals,
|
|
371
|
+
'SCREENSHARE',
|
|
372
|
+
`${SCREENSHARE_JOURNEY} screen share stop callback handling completed`,
|
|
373
|
+
{
|
|
374
|
+
action: 'stop',
|
|
375
|
+
stage: 'complete',
|
|
376
|
+
outcome: 'success',
|
|
377
|
+
screenshareAttemptId,
|
|
378
|
+
screenshareSessionId,
|
|
379
|
+
recordingActive: props.isRecordingActive,
|
|
380
|
+
screenShareUid,
|
|
381
|
+
stopOrigin,
|
|
382
|
+
stopActorUid,
|
|
383
|
+
elapsedMs: Date.now() - callbackStartedAt,
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
activeScreenshareSessionIdRef.current = null;
|
|
268
387
|
};
|
|
269
388
|
|
|
270
389
|
useEffect(() => {
|
|
@@ -275,54 +394,267 @@ export const ScreenshareConfigure = (props: {
|
|
|
275
394
|
);
|
|
276
395
|
}, []);
|
|
277
396
|
|
|
278
|
-
const executeRecordingQuery = (
|
|
397
|
+
const executeRecordingQuery = async (
|
|
398
|
+
isScreenActive: boolean,
|
|
399
|
+
screenshareAttemptId: string,
|
|
400
|
+
screenshareSessionId: string,
|
|
401
|
+
stopOrigin: ScreenshareStopOrigin,
|
|
402
|
+
stopActorUid?: UidType,
|
|
403
|
+
) => {
|
|
404
|
+
const action: ScreenshareAction = isScreenActive ? 'start' : 'stop';
|
|
279
405
|
if (isScreenActive) {
|
|
280
406
|
// If recording is going on, set the presenter query
|
|
281
407
|
logger.log(
|
|
282
408
|
LogSource.Internals,
|
|
283
409
|
'SCREENSHARE',
|
|
284
|
-
|
|
410
|
+
`${SCREENSHARE_JOURNEY} screen share start recording presenter layout query started`,
|
|
411
|
+
{
|
|
412
|
+
action,
|
|
413
|
+
stage: 'recording_layout',
|
|
414
|
+
outcome: 'started',
|
|
415
|
+
screenshareAttemptId,
|
|
416
|
+
screenshareSessionId,
|
|
417
|
+
recordingActive: props.isRecordingActive,
|
|
418
|
+
screenShareUid,
|
|
419
|
+
stopOrigin,
|
|
420
|
+
stopActorUid,
|
|
421
|
+
},
|
|
285
422
|
);
|
|
286
|
-
executePresenterQuery(screenShareUid);
|
|
423
|
+
await executePresenterQuery(screenShareUid);
|
|
287
424
|
} else {
|
|
288
425
|
logger.log(
|
|
289
426
|
LogSource.Internals,
|
|
290
427
|
'SCREENSHARE',
|
|
291
|
-
|
|
428
|
+
`${SCREENSHARE_JOURNEY} screen share stop recording normal layout query started`,
|
|
429
|
+
{
|
|
430
|
+
action,
|
|
431
|
+
stage: 'recording_layout',
|
|
432
|
+
outcome: 'started',
|
|
433
|
+
screenshareAttemptId,
|
|
434
|
+
screenshareSessionId,
|
|
435
|
+
recordingActive: props.isRecordingActive,
|
|
436
|
+
screenShareUid,
|
|
437
|
+
stopOrigin,
|
|
438
|
+
stopActorUid,
|
|
439
|
+
},
|
|
292
440
|
);
|
|
293
441
|
// If no recording is going on, set the normal query
|
|
294
|
-
executeNormalQuery();
|
|
442
|
+
await executeNormalQuery();
|
|
295
443
|
}
|
|
444
|
+
logger.log(
|
|
445
|
+
LogSource.Internals,
|
|
446
|
+
'SCREENSHARE',
|
|
447
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} recording layout query completed successfully`,
|
|
448
|
+
{
|
|
449
|
+
action,
|
|
450
|
+
stage: 'recording_layout',
|
|
451
|
+
outcome: 'success',
|
|
452
|
+
screenshareAttemptId,
|
|
453
|
+
screenshareSessionId,
|
|
454
|
+
recordingActive: props.isRecordingActive,
|
|
455
|
+
screenShareUid,
|
|
456
|
+
stopOrigin,
|
|
457
|
+
stopActorUid,
|
|
458
|
+
},
|
|
459
|
+
);
|
|
296
460
|
};
|
|
297
461
|
|
|
298
|
-
const stopScreenshare = (
|
|
462
|
+
const stopScreenshare = async (
|
|
463
|
+
stopOrigin: ScreenshareStopOrigin = 'unknown',
|
|
464
|
+
stopActorUid?: UidType,
|
|
465
|
+
) => {
|
|
466
|
+
const screenshareAttemptId = getUniqueID();
|
|
467
|
+
const screenshareSessionId = getScreenshareSessionId(
|
|
468
|
+
'stop',
|
|
469
|
+
activeScreenshareSessionIdRef.current,
|
|
470
|
+
getUniqueID,
|
|
471
|
+
);
|
|
472
|
+
logger.log(
|
|
473
|
+
LogSource.Internals,
|
|
474
|
+
'SCREENSHARE',
|
|
475
|
+
`${SCREENSHARE_JOURNEY} screen share stop requested from ${stopOrigin}`,
|
|
476
|
+
{
|
|
477
|
+
action: 'stop',
|
|
478
|
+
stage: 'ui_request',
|
|
479
|
+
outcome: 'started',
|
|
480
|
+
screenshareAttemptId,
|
|
481
|
+
screenshareSessionId,
|
|
482
|
+
recordingActive: props.isRecordingActive,
|
|
483
|
+
screenShareUid,
|
|
484
|
+
stopOrigin,
|
|
485
|
+
stopActorUid,
|
|
486
|
+
},
|
|
487
|
+
);
|
|
299
488
|
if (!isScreenshareActive) {
|
|
489
|
+
logger.log(
|
|
490
|
+
LogSource.Internals,
|
|
491
|
+
'SCREENSHARE',
|
|
492
|
+
`${SCREENSHARE_JOURNEY} screen share stop skipped because screen share is already inactive`,
|
|
493
|
+
{
|
|
494
|
+
action: 'stop',
|
|
495
|
+
stage: 'precondition',
|
|
496
|
+
outcome: 'skipped',
|
|
497
|
+
screenshareAttemptId,
|
|
498
|
+
screenshareSessionId,
|
|
499
|
+
recordingActive: props.isRecordingActive,
|
|
500
|
+
screenShareUid,
|
|
501
|
+
stopOrigin,
|
|
502
|
+
stopActorUid,
|
|
503
|
+
},
|
|
504
|
+
);
|
|
300
505
|
return;
|
|
301
506
|
}
|
|
302
|
-
userScreenshare(
|
|
507
|
+
await userScreenshare(
|
|
508
|
+
false,
|
|
509
|
+
screenshareAttemptId,
|
|
510
|
+
screenshareSessionId,
|
|
511
|
+
stopOrigin,
|
|
512
|
+
stopActorUid,
|
|
513
|
+
);
|
|
303
514
|
};
|
|
304
|
-
const startScreenshare = () => {
|
|
515
|
+
const startScreenshare = async () => {
|
|
516
|
+
const screenshareAttemptId = getUniqueID();
|
|
517
|
+
const screenshareSessionId = getScreenshareSessionId(
|
|
518
|
+
'start',
|
|
519
|
+
activeScreenshareSessionIdRef.current,
|
|
520
|
+
getUniqueID,
|
|
521
|
+
);
|
|
522
|
+
logger.log(
|
|
523
|
+
LogSource.Internals,
|
|
524
|
+
'SCREENSHARE',
|
|
525
|
+
`${SCREENSHARE_JOURNEY} screen share start requested from UI`,
|
|
526
|
+
{
|
|
527
|
+
action: 'start',
|
|
528
|
+
stage: 'ui_request',
|
|
529
|
+
outcome: 'started',
|
|
530
|
+
screenshareAttemptId,
|
|
531
|
+
screenshareSessionId,
|
|
532
|
+
recordingActive: props.isRecordingActive,
|
|
533
|
+
screenShareUid,
|
|
534
|
+
stopOrigin: 'unknown',
|
|
535
|
+
},
|
|
536
|
+
);
|
|
305
537
|
if (isScreenshareActive) {
|
|
538
|
+
logger.log(
|
|
539
|
+
LogSource.Internals,
|
|
540
|
+
'SCREENSHARE',
|
|
541
|
+
`${SCREENSHARE_JOURNEY} screen share start skipped because screen share is already active`,
|
|
542
|
+
{
|
|
543
|
+
action: 'start',
|
|
544
|
+
stage: 'precondition',
|
|
545
|
+
outcome: 'skipped',
|
|
546
|
+
screenshareAttemptId,
|
|
547
|
+
screenshareSessionId,
|
|
548
|
+
recordingActive: props.isRecordingActive,
|
|
549
|
+
screenShareUid,
|
|
550
|
+
stopOrigin: 'unknown',
|
|
551
|
+
},
|
|
552
|
+
);
|
|
306
553
|
return;
|
|
307
554
|
}
|
|
308
|
-
userScreenshare(
|
|
555
|
+
await userScreenshare(
|
|
556
|
+
true,
|
|
557
|
+
screenshareAttemptId,
|
|
558
|
+
screenshareSessionId,
|
|
559
|
+
'unknown',
|
|
560
|
+
);
|
|
309
561
|
};
|
|
562
|
+
stopScreenshareRef.current = stopScreenshare;
|
|
310
563
|
|
|
311
|
-
const userScreenshare = async (
|
|
564
|
+
const userScreenshare = async (
|
|
565
|
+
isActive: boolean,
|
|
566
|
+
screenshareAttemptId: string,
|
|
567
|
+
screenshareSessionId: string,
|
|
568
|
+
stopOrigin: ScreenshareStopOrigin,
|
|
569
|
+
stopActorUid?: UidType,
|
|
570
|
+
) => {
|
|
571
|
+
const startedAt = Date.now();
|
|
572
|
+
const action: ScreenshareAction = isActive ? 'start' : 'stop';
|
|
573
|
+
let stage = 'recording_layout';
|
|
574
|
+
let recordingLayoutFailed = false;
|
|
312
575
|
logger.log(
|
|
313
576
|
LogSource.Internals,
|
|
314
577
|
'SCREENSHARE',
|
|
315
|
-
`${
|
|
578
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} workflow started`,
|
|
316
579
|
{
|
|
317
|
-
|
|
580
|
+
action,
|
|
581
|
+
stage: 'journey',
|
|
582
|
+
outcome: 'started',
|
|
583
|
+
screenshareAttemptId,
|
|
584
|
+
screenshareSessionId,
|
|
585
|
+
recordingActive: props.isRecordingActive,
|
|
318
586
|
screenShareUid,
|
|
319
|
-
|
|
587
|
+
stopOrigin,
|
|
588
|
+
stopActorUid,
|
|
589
|
+
channel,
|
|
320
590
|
},
|
|
321
591
|
);
|
|
322
592
|
try {
|
|
323
593
|
if (props.isRecordingActive) {
|
|
324
|
-
|
|
594
|
+
try {
|
|
595
|
+
await executeRecordingQuery(
|
|
596
|
+
isActive,
|
|
597
|
+
screenshareAttemptId,
|
|
598
|
+
screenshareSessionId,
|
|
599
|
+
stopOrigin,
|
|
600
|
+
stopActorUid,
|
|
601
|
+
);
|
|
602
|
+
} catch (recordingError) {
|
|
603
|
+
recordingLayoutFailed = true;
|
|
604
|
+
logger.error(
|
|
605
|
+
LogSource.Internals,
|
|
606
|
+
'SCREENSHARE',
|
|
607
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} recording layout query failed; continuing screen share ${action}`,
|
|
608
|
+
recordingError,
|
|
609
|
+
{
|
|
610
|
+
action,
|
|
611
|
+
stage,
|
|
612
|
+
outcome: 'partial_failure',
|
|
613
|
+
screenshareAttemptId,
|
|
614
|
+
screenshareSessionId,
|
|
615
|
+
recordingActive: props.isRecordingActive,
|
|
616
|
+
screenShareUid,
|
|
617
|
+
stopOrigin,
|
|
618
|
+
stopActorUid,
|
|
619
|
+
...getScreenshareError(recordingError),
|
|
620
|
+
},
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
} else {
|
|
624
|
+
logger.log(
|
|
625
|
+
LogSource.Internals,
|
|
626
|
+
'SCREENSHARE',
|
|
627
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} recording layout query skipped because recording is inactive`,
|
|
628
|
+
{
|
|
629
|
+
action,
|
|
630
|
+
stage,
|
|
631
|
+
outcome: 'skipped',
|
|
632
|
+
screenshareAttemptId,
|
|
633
|
+
screenshareSessionId,
|
|
634
|
+
recordingActive: false,
|
|
635
|
+
screenShareUid,
|
|
636
|
+
stopOrigin,
|
|
637
|
+
stopActorUid,
|
|
638
|
+
},
|
|
639
|
+
);
|
|
325
640
|
}
|
|
641
|
+
stage = 'rtc_operation';
|
|
642
|
+
logger.log(
|
|
643
|
+
LogSource.Internals,
|
|
644
|
+
'SCREENSHARE',
|
|
645
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} calling RtcEngine.startScreenshare`,
|
|
646
|
+
{
|
|
647
|
+
action,
|
|
648
|
+
stage,
|
|
649
|
+
outcome: 'started',
|
|
650
|
+
screenshareAttemptId,
|
|
651
|
+
screenshareSessionId,
|
|
652
|
+
recordingActive: props.isRecordingActive,
|
|
653
|
+
screenShareUid,
|
|
654
|
+
stopOrigin,
|
|
655
|
+
stopActorUid,
|
|
656
|
+
},
|
|
657
|
+
);
|
|
326
658
|
// @ts-ignore
|
|
327
659
|
await rtc.RtcEngineUnsafe.startScreenshare(
|
|
328
660
|
screenShareToken,
|
|
@@ -333,11 +665,56 @@ export const ScreenshareConfigure = (props: {
|
|
|
333
665
|
rtc.RtcEngineUnsafe as unknown as IAgoraRTC,
|
|
334
666
|
encryption as unknown as any,
|
|
335
667
|
{encoderConfig: '1080p_2', optimizationMode: 'detail'},
|
|
668
|
+
'auto',
|
|
669
|
+
{
|
|
670
|
+
action,
|
|
671
|
+
screenshareAttemptId,
|
|
672
|
+
screenshareSessionId,
|
|
673
|
+
recordingActive: props.isRecordingActive,
|
|
674
|
+
screenShareUid,
|
|
675
|
+
stopOrigin,
|
|
676
|
+
stopActorUid,
|
|
677
|
+
},
|
|
678
|
+
);
|
|
679
|
+
logger.log(
|
|
680
|
+
LogSource.Internals,
|
|
681
|
+
'SCREENSHARE',
|
|
682
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} RtcEngine.startScreenshare completed successfully`,
|
|
683
|
+
{
|
|
684
|
+
action,
|
|
685
|
+
stage,
|
|
686
|
+
outcome: 'success',
|
|
687
|
+
screenshareAttemptId,
|
|
688
|
+
screenshareSessionId,
|
|
689
|
+
recordingActive: props.isRecordingActive,
|
|
690
|
+
screenShareUid,
|
|
691
|
+
stopOrigin,
|
|
692
|
+
stopActorUid,
|
|
693
|
+
},
|
|
336
694
|
);
|
|
337
695
|
isActive && setScreenshareActive(true);
|
|
338
696
|
|
|
339
697
|
if (isActive) {
|
|
698
|
+
activeScreenshareSessionIdRef.current = screenshareSessionId;
|
|
699
|
+
stage = 'local_active_state';
|
|
700
|
+
logger.log(
|
|
701
|
+
LogSource.Internals,
|
|
702
|
+
'SCREENSHARE',
|
|
703
|
+
`${SCREENSHARE_JOURNEY} screen share start local active state set to active`,
|
|
704
|
+
{
|
|
705
|
+
action,
|
|
706
|
+
stage,
|
|
707
|
+
outcome: 'success',
|
|
708
|
+
screenshareAttemptId,
|
|
709
|
+
screenshareSessionId,
|
|
710
|
+
recordingActive: props.isRecordingActive,
|
|
711
|
+
screenShareUid,
|
|
712
|
+
stopOrigin,
|
|
713
|
+
stopActorUid,
|
|
714
|
+
},
|
|
715
|
+
);
|
|
340
716
|
// 1. Set local state
|
|
717
|
+
stage = 'screenshare_context';
|
|
341
718
|
setScreenShareData(prevState => {
|
|
342
719
|
return {
|
|
343
720
|
...prevState,
|
|
@@ -349,7 +726,24 @@ export const ScreenshareConfigure = (props: {
|
|
|
349
726
|
},
|
|
350
727
|
};
|
|
351
728
|
});
|
|
729
|
+
logger.log(
|
|
730
|
+
LogSource.Internals,
|
|
731
|
+
'SCREENSHARE',
|
|
732
|
+
`${SCREENSHARE_JOURNEY} screen share start local context set to active`,
|
|
733
|
+
{
|
|
734
|
+
action,
|
|
735
|
+
stage,
|
|
736
|
+
outcome: 'success',
|
|
737
|
+
screenshareAttemptId,
|
|
738
|
+
screenshareSessionId,
|
|
739
|
+
recordingActive: props.isRecordingActive,
|
|
740
|
+
screenShareUid,
|
|
741
|
+
stopOrigin,
|
|
742
|
+
stopActorUid,
|
|
743
|
+
},
|
|
744
|
+
);
|
|
352
745
|
// 2. Inform everyone in the channel screenshare is actice
|
|
746
|
+
stage = 'rtm_event';
|
|
353
747
|
events.send(
|
|
354
748
|
EventNames.SCREENSHARE_ATTRIBUTE,
|
|
355
749
|
JSON.stringify({
|
|
@@ -359,23 +753,81 @@ export const ScreenshareConfigure = (props: {
|
|
|
359
753
|
}),
|
|
360
754
|
PersistanceLevel.Sender,
|
|
361
755
|
);
|
|
756
|
+
logger.log(
|
|
757
|
+
LogSource.Internals,
|
|
758
|
+
'SCREENSHARE',
|
|
759
|
+
`${SCREENSHARE_JOURNEY} screen share start RTM event sent to remote users`,
|
|
760
|
+
{
|
|
761
|
+
action,
|
|
762
|
+
stage,
|
|
763
|
+
outcome: 'success',
|
|
764
|
+
screenshareAttemptId,
|
|
765
|
+
screenshareSessionId,
|
|
766
|
+
recordingActive: props.isRecordingActive,
|
|
767
|
+
screenShareUid,
|
|
768
|
+
stopOrigin,
|
|
769
|
+
stopActorUid,
|
|
770
|
+
},
|
|
771
|
+
);
|
|
362
772
|
}
|
|
773
|
+
logger.log(
|
|
774
|
+
LogSource.Internals,
|
|
775
|
+
'SCREENSHARE',
|
|
776
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} workflow ${
|
|
777
|
+
recordingLayoutFailed
|
|
778
|
+
? 'completed with recording layout failure'
|
|
779
|
+
: 'completed successfully'
|
|
780
|
+
}`,
|
|
781
|
+
{
|
|
782
|
+
action,
|
|
783
|
+
stage: 'complete',
|
|
784
|
+
outcome: recordingLayoutFailed ? 'partial_success' : 'success',
|
|
785
|
+
screenshareAttemptId,
|
|
786
|
+
screenshareSessionId,
|
|
787
|
+
recordingActive: props.isRecordingActive,
|
|
788
|
+
screenShareUid,
|
|
789
|
+
stopOrigin,
|
|
790
|
+
stopActorUid,
|
|
791
|
+
elapsedMs: Date.now() - startedAt,
|
|
792
|
+
},
|
|
793
|
+
);
|
|
363
794
|
} catch (e) {
|
|
795
|
+
const userCancelOrPermissionDenied = isUserCancelOrPermissionDenied(e);
|
|
364
796
|
logger.error(
|
|
365
797
|
LogSource.Internals,
|
|
366
798
|
'SCREENSHARE',
|
|
367
|
-
|
|
368
|
-
|
|
799
|
+
`${SCREENSHARE_JOURNEY} screen share ${action} workflow failed at ${stage}: ${
|
|
800
|
+
userCancelOrPermissionDenied
|
|
801
|
+
? 'user cancelled picker or permission was denied'
|
|
802
|
+
: 'unexpected failure'
|
|
803
|
+
}`,
|
|
804
|
+
{
|
|
805
|
+
action,
|
|
806
|
+
stage,
|
|
807
|
+
outcome: userCancelOrPermissionDenied
|
|
808
|
+
? 'user_cancel_or_permission_denied'
|
|
809
|
+
: 'failure',
|
|
810
|
+
screenshareAttemptId,
|
|
811
|
+
screenshareSessionId,
|
|
812
|
+
recordingActive: props.isRecordingActive,
|
|
813
|
+
screenShareUid,
|
|
814
|
+
stopOrigin,
|
|
815
|
+
stopActorUid,
|
|
816
|
+
elapsedMs: Date.now() - startedAt,
|
|
817
|
+
...getScreenshareError(e),
|
|
818
|
+
},
|
|
369
819
|
);
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
820
|
+
if (!userCancelOrPermissionDenied) {
|
|
821
|
+
Toast.show({
|
|
822
|
+
leadingIconName: 'alert',
|
|
823
|
+
type: 'error',
|
|
824
|
+
text1: toastHeading,
|
|
825
|
+
text2: toastSubHeading,
|
|
826
|
+
visibilityTime: 1000 * 10,
|
|
827
|
+
primaryBtn: null,
|
|
828
|
+
secondaryBtn: null,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
379
831
|
}
|
|
380
832
|
};
|
|
381
833
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getScreenshareError,
|
|
3
|
+
getScreenshareReleaseOrigin,
|
|
4
|
+
getScreenshareSessionId,
|
|
5
|
+
isUserCancelOrPermissionDenied,
|
|
6
|
+
SCREENSHARE_JOURNEY,
|
|
7
|
+
} from '../screenshareJourney';
|
|
8
|
+
|
|
9
|
+
describe('screenshare journey logging', () => {
|
|
10
|
+
it('uses the Datadog filter prefix', () => {
|
|
11
|
+
expect(SCREENSHARE_JOURNEY).toBe('[SCREENSHARE_JOURNEY]');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it.each([
|
|
15
|
+
[{code: 'PERMISSION_DENIED'}, true],
|
|
16
|
+
[{name: 'NotAllowedError'}, true],
|
|
17
|
+
[{message: 'AgoraRTCError PERMISSION_DENIED'}, true],
|
|
18
|
+
[{code: 'NETWORK_ERROR'}, false],
|
|
19
|
+
])('classifies permission and cancellation errors', (error, expected) => {
|
|
20
|
+
expect(isUserCancelOrPermissionDenied(error)).toBe(expected);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('normalizes SDK error details for structured logs', () => {
|
|
24
|
+
expect(
|
|
25
|
+
getScreenshareError({
|
|
26
|
+
code: 'PERMISSION_DENIED',
|
|
27
|
+
name: 'AgoraRTCError',
|
|
28
|
+
message: 'NotAllowedError: Permission denied by user',
|
|
29
|
+
}),
|
|
30
|
+
).toEqual({
|
|
31
|
+
sdkErrorCode: 'PERMISSION_DENIED',
|
|
32
|
+
sdkErrorName: 'AgoraRTCError',
|
|
33
|
+
sdkErrorMessage: 'NotAllowedError: Permission denied by user',
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('reuses the active screen-share session ID when stopping', () => {
|
|
38
|
+
const createId = jest.fn(() => 'new-session-id');
|
|
39
|
+
|
|
40
|
+
expect(getScreenshareSessionId('stop', 'active-session-id', createId)).toBe(
|
|
41
|
+
'active-session-id',
|
|
42
|
+
);
|
|
43
|
+
expect(createId).not.toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('creates a new screen-share session ID when starting', () => {
|
|
47
|
+
const createId = jest.fn(() => 'new-session-id');
|
|
48
|
+
|
|
49
|
+
expect(getScreenshareSessionId('start', null, createId)).toBe(
|
|
50
|
+
'new-session-id',
|
|
51
|
+
);
|
|
52
|
+
expect(createId).toHaveBeenCalledTimes(1);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('classifies hidden-document release as page unload', () => {
|
|
56
|
+
expect(getScreenshareReleaseOrigin(undefined, 'hidden')).toBe(
|
|
57
|
+
'page_unload',
|
|
58
|
+
);
|
|
59
|
+
expect(getScreenshareReleaseOrigin(undefined, 'visible')).toBe(
|
|
60
|
+
'end_call_cleanup',
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|