agora-appbuilder-core 4.1.23 → 4.1.24-beta.2
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 +86 -49
- package/template/bridge/rtc/webNg/__tests__/screenshareLifecycle.test.ts +50 -21
- package/template/defaultConfig.js +2 -2
- package/template/src/components/PinnedVideo.tsx +43 -23
- package/template/src/components/__tests__/pinnedLayoutState.test.ts +47 -0
- package/template/src/components/participants/UserActionMenuOptions.tsx +15 -2
- package/template/src/components/pinnedLayoutState.ts +43 -0
- package/template/src/pages/video-call/VideoRenderer.tsx +2 -0
- package/template/src/subComponents/caption/Transcript.tsx +36 -0
- package/template/src/subComponents/caption/__tests__/transcriptJourney.test.ts +7 -0
- package/template/src/subComponents/caption/transcriptJourney.ts +1 -0
- package/template/src/subComponents/caption/useCaption.tsx +215 -46
- package/template/src/subComponents/caption/useSTTAPI.tsx +13 -12
- package/template/src/subComponents/caption/useStreamMessageUtils.ts +109 -8
- package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +115 -30
- package/template/src/subComponents/screenshare/ScreenshareConfigure.tsx +489 -147
- package/template/src/subComponents/screenshare/__tests__/ScreenshareConfigure.test.tsx +245 -0
- package/template/src/subComponents/screenshare/__tests__/screenshareInterruptionRecovery.test.ts +119 -0
- package/template/src/subComponents/screenshare/screenshareInterruptionRecovery.ts +118 -0
|
@@ -55,6 +55,11 @@ import {
|
|
|
55
55
|
getScreenshareStopDecision,
|
|
56
56
|
} from './screenshareOperation';
|
|
57
57
|
import {createScreenshareRecordingLayoutReconciler} from './screenshareRecordingLayoutReconciler';
|
|
58
|
+
import {
|
|
59
|
+
captureScreenshareRecoveryCandidate,
|
|
60
|
+
getScreenshareRecoveryDecision,
|
|
61
|
+
ScreenshareRecoveryCandidate,
|
|
62
|
+
} from './screenshareInterruptionRecovery';
|
|
58
63
|
|
|
59
64
|
type ScreenshareAction = 'start' | 'stop';
|
|
60
65
|
|
|
@@ -93,6 +98,14 @@ export const ScreenshareConfigure = (props: {
|
|
|
93
98
|
const changeLayout = useChangeDefaultLayout();
|
|
94
99
|
const {currentLayout} = useLayout();
|
|
95
100
|
const currentLayoutRef = useRef({currentLayout: currentLayout});
|
|
101
|
+
const recoveryCandidateRef = useRef<ScreenshareRecoveryCandidate | null>(
|
|
102
|
+
null,
|
|
103
|
+
);
|
|
104
|
+
const previousLayoutStateRef = useRef({
|
|
105
|
+
activeUids,
|
|
106
|
+
pinnedUid,
|
|
107
|
+
currentLayout,
|
|
108
|
+
});
|
|
96
109
|
|
|
97
110
|
const updateOperationState = (state: ScreenshareOperationState) => {
|
|
98
111
|
operationStateRef.current = state;
|
|
@@ -201,6 +214,142 @@ export const ScreenshareConfigure = (props: {
|
|
|
201
214
|
}
|
|
202
215
|
};
|
|
203
216
|
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
const previousLayoutState = previousLayoutStateRef.current;
|
|
219
|
+
const existingCandidate = recoveryCandidateRef.current;
|
|
220
|
+
// Detect when the previously pinned screen-share UID disappears from the
|
|
221
|
+
// RTC render list while RTM still says the logical share is active.
|
|
222
|
+
const capturedCandidate = captureScreenshareRecoveryCandidate({
|
|
223
|
+
previousActiveUids: previousLayoutState.activeUids,
|
|
224
|
+
currentActiveUids: activeUids,
|
|
225
|
+
previousPinnedUid: previousLayoutState.pinnedUid,
|
|
226
|
+
previousLayout: previousLayoutState.currentLayout,
|
|
227
|
+
screenShareData,
|
|
228
|
+
detectedAt: Date.now(),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!existingCandidate && capturedCandidate) {
|
|
232
|
+
recoveryCandidateRef.current = capturedCandidate;
|
|
233
|
+
logger.log(
|
|
234
|
+
LogSource.Internals,
|
|
235
|
+
'SCREENSHARE',
|
|
236
|
+
`${SCREENSHARE_JOURNEY} pinned screen share removed by RTC; waiting for same UID to recover`,
|
|
237
|
+
{
|
|
238
|
+
action: 'recover',
|
|
239
|
+
stage: 'rtc_interruption',
|
|
240
|
+
outcome: 'started',
|
|
241
|
+
screenshareSessionId:
|
|
242
|
+
activeScreenshareSessionIdRef.current || 'unknown-session',
|
|
243
|
+
screenShareUid: capturedCandidate.uid,
|
|
244
|
+
previousPinnedUid: capturedCandidate.previousPinnedUid,
|
|
245
|
+
previousLayout: capturedCandidate.previousLayout,
|
|
246
|
+
},
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const candidate = recoveryCandidateRef.current;
|
|
251
|
+
if (candidate) {
|
|
252
|
+
// Recovery is driven by render state: first wait for the same UID to
|
|
253
|
+
// rejoin, then wait for its video to be published before restoring it.
|
|
254
|
+
const decision = getScreenshareRecoveryDecision({
|
|
255
|
+
candidate,
|
|
256
|
+
activeUids,
|
|
257
|
+
pinnedUid,
|
|
258
|
+
isVideoPublished: defaultContent?.[candidate.uid]?.video === 1,
|
|
259
|
+
screenShareData,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (decision === 'waiting_for_video' && !candidate.joinedLogged) {
|
|
263
|
+
// The RTC user is back, but restoring now could show a blank tile.
|
|
264
|
+
candidate.joinedLogged = true;
|
|
265
|
+
logger.log(
|
|
266
|
+
LogSource.Internals,
|
|
267
|
+
'SCREENSHARE',
|
|
268
|
+
`${SCREENSHARE_JOURNEY} interrupted screen share UID rejoined; waiting for video publication`,
|
|
269
|
+
{
|
|
270
|
+
action: 'recover',
|
|
271
|
+
stage: 'rtc_rejoin',
|
|
272
|
+
outcome: 'waiting',
|
|
273
|
+
screenshareSessionId:
|
|
274
|
+
activeScreenshareSessionIdRef.current || 'unknown-session',
|
|
275
|
+
screenShareUid: candidate.uid,
|
|
276
|
+
elapsedMs: Date.now() - candidate.detectedAt,
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
} else if (decision === 'restore') {
|
|
280
|
+
// Restore only the layout state that existed before interruption. A
|
|
281
|
+
// newer user pin or screen-share session cancels this path below.
|
|
282
|
+
isPinned.current = candidate.uid;
|
|
283
|
+
dispatch({type: 'UserPin', value: [candidate.uid]});
|
|
284
|
+
if (
|
|
285
|
+
candidate.previousLayout === getPinnedLayoutName() &&
|
|
286
|
+
currentLayout !== getPinnedLayoutName()
|
|
287
|
+
) {
|
|
288
|
+
setPinnedLayout();
|
|
289
|
+
}
|
|
290
|
+
recoveryCandidateRef.current = null;
|
|
291
|
+
logger.log(
|
|
292
|
+
LogSource.Internals,
|
|
293
|
+
'SCREENSHARE',
|
|
294
|
+
`${SCREENSHARE_JOURNEY} interrupted screen share video republished; previous pin restored`,
|
|
295
|
+
{
|
|
296
|
+
action: 'recover',
|
|
297
|
+
stage: 'layout_restore',
|
|
298
|
+
outcome: 'success',
|
|
299
|
+
screenshareSessionId:
|
|
300
|
+
activeScreenshareSessionIdRef.current || 'unknown-session',
|
|
301
|
+
screenShareUid: candidate.uid,
|
|
302
|
+
restoredPinnedUid: candidate.previousPinnedUid,
|
|
303
|
+
restoredLayout: candidate.previousLayout,
|
|
304
|
+
elapsedMs: Date.now() - candidate.detectedAt,
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
} else if (decision.startsWith('cancel_')) {
|
|
308
|
+
// An authoritative stop, replacement share, or user layout choice
|
|
309
|
+
// takes precedence over automatic interruption recovery.
|
|
310
|
+
recoveryCandidateRef.current = null;
|
|
311
|
+
logger.log(
|
|
312
|
+
LogSource.Internals,
|
|
313
|
+
'SCREENSHARE',
|
|
314
|
+
`${SCREENSHARE_JOURNEY} interrupted screen share pin restoration skipped`,
|
|
315
|
+
{
|
|
316
|
+
action: 'recover',
|
|
317
|
+
stage: 'layout_restore',
|
|
318
|
+
outcome: 'skipped',
|
|
319
|
+
screenshareSessionId:
|
|
320
|
+
activeScreenshareSessionIdRef.current || 'unknown-session',
|
|
321
|
+
screenShareUid: candidate.uid,
|
|
322
|
+
skipReason: decision,
|
|
323
|
+
currentPinnedUid: pinnedUid,
|
|
324
|
+
currentLayout,
|
|
325
|
+
elapsedMs: Date.now() - candidate.detectedAt,
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
previousLayoutStateRef.current = {
|
|
332
|
+
activeUids,
|
|
333
|
+
pinnedUid,
|
|
334
|
+
currentLayout,
|
|
335
|
+
};
|
|
336
|
+
}, [
|
|
337
|
+
activeUids,
|
|
338
|
+
currentLayout,
|
|
339
|
+
defaultContent,
|
|
340
|
+
dispatch,
|
|
341
|
+
pinnedUid,
|
|
342
|
+
screenShareData,
|
|
343
|
+
setPinnedLayout,
|
|
344
|
+
]);
|
|
345
|
+
|
|
346
|
+
useEffect(
|
|
347
|
+
() => () => {
|
|
348
|
+
recoveryCandidateRef.current = null;
|
|
349
|
+
},
|
|
350
|
+
[],
|
|
351
|
+
);
|
|
352
|
+
|
|
204
353
|
useEffect(() => {
|
|
205
354
|
const unsubKickScreenshare = events.on(
|
|
206
355
|
controlMessageEnum.kickScreenshare,
|
|
@@ -452,63 +601,80 @@ export const ScreenshareConfigure = (props: {
|
|
|
452
601
|
);
|
|
453
602
|
}, []);
|
|
454
603
|
|
|
455
|
-
const
|
|
456
|
-
isScreenActive: boolean,
|
|
604
|
+
const executeStartRecordingLayoutQuery = async (
|
|
457
605
|
screenshareAttemptId: string,
|
|
458
606
|
screenshareSessionId: string,
|
|
459
607
|
stopOrigin: ScreenshareStopOrigin,
|
|
460
608
|
stopActorUid?: UidType,
|
|
461
609
|
) => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
'
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
stage: 'recording_layout',
|
|
472
|
-
outcome: 'started',
|
|
473
|
-
screenshareAttemptId,
|
|
474
|
-
screenshareSessionId,
|
|
475
|
-
recordingActive: props.isRecordingActive,
|
|
476
|
-
screenShareUid,
|
|
477
|
-
stopOrigin,
|
|
478
|
-
stopActorUid,
|
|
479
|
-
},
|
|
480
|
-
);
|
|
481
|
-
await executePresenterQuery(screenShareUid);
|
|
482
|
-
recordingLayoutReconcilerRef.current.markApplied(
|
|
610
|
+
logger.log(
|
|
611
|
+
LogSource.Internals,
|
|
612
|
+
'SCREENSHARE',
|
|
613
|
+
`${SCREENSHARE_JOURNEY} screen share start recording presenter layout query started`,
|
|
614
|
+
{
|
|
615
|
+
action: 'start',
|
|
616
|
+
stage: 'recording_layout',
|
|
617
|
+
outcome: 'started',
|
|
618
|
+
screenshareAttemptId,
|
|
483
619
|
screenshareSessionId,
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
620
|
+
recordingActive: props.isRecordingActive,
|
|
621
|
+
screenShareUid,
|
|
622
|
+
stopOrigin,
|
|
623
|
+
stopActorUid,
|
|
624
|
+
},
|
|
625
|
+
);
|
|
626
|
+
await executePresenterQuery(screenShareUid);
|
|
627
|
+
recordingLayoutReconcilerRef.current.markApplied(
|
|
628
|
+
screenshareSessionId,
|
|
629
|
+
recordingActiveRef.current,
|
|
630
|
+
);
|
|
631
|
+
logger.log(
|
|
632
|
+
LogSource.Internals,
|
|
633
|
+
'SCREENSHARE',
|
|
634
|
+
`${SCREENSHARE_JOURNEY} screen share start recording layout query completed successfully`,
|
|
635
|
+
{
|
|
636
|
+
action: 'start',
|
|
637
|
+
stage: 'recording_layout',
|
|
638
|
+
outcome: 'success',
|
|
639
|
+
screenshareAttemptId,
|
|
640
|
+
screenshareSessionId,
|
|
641
|
+
recordingActive: props.isRecordingActive,
|
|
642
|
+
screenShareUid,
|
|
643
|
+
stopOrigin,
|
|
644
|
+
stopActorUid,
|
|
645
|
+
},
|
|
646
|
+
);
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const executeStopRecordingLayoutQuery = async (
|
|
650
|
+
screenshareAttemptId: string,
|
|
651
|
+
screenshareSessionId: string,
|
|
652
|
+
stopOrigin: ScreenshareStopOrigin,
|
|
653
|
+
stopActorUid?: UidType,
|
|
654
|
+
) => {
|
|
655
|
+
logger.log(
|
|
656
|
+
LogSource.Internals,
|
|
657
|
+
'SCREENSHARE',
|
|
658
|
+
`${SCREENSHARE_JOURNEY} screen share stop recording normal layout query started`,
|
|
659
|
+
{
|
|
660
|
+
action: 'stop',
|
|
661
|
+
stage: 'recording_layout',
|
|
662
|
+
outcome: 'started',
|
|
663
|
+
screenshareAttemptId,
|
|
664
|
+
screenshareSessionId,
|
|
665
|
+
recordingActive: props.isRecordingActive,
|
|
666
|
+
screenShareUid,
|
|
667
|
+
stopOrigin,
|
|
668
|
+
stopActorUid,
|
|
669
|
+
},
|
|
670
|
+
);
|
|
671
|
+
await executeNormalQuery();
|
|
506
672
|
logger.log(
|
|
507
673
|
LogSource.Internals,
|
|
508
674
|
'SCREENSHARE',
|
|
509
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
675
|
+
`${SCREENSHARE_JOURNEY} screen share stop recording layout query completed successfully`,
|
|
510
676
|
{
|
|
511
|
-
action,
|
|
677
|
+
action: 'stop',
|
|
512
678
|
stage: 'recording_layout',
|
|
513
679
|
outcome: 'success',
|
|
514
680
|
screenshareAttemptId,
|
|
@@ -744,8 +910,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
744
910
|
return;
|
|
745
911
|
}
|
|
746
912
|
updateOperationState('stopping');
|
|
747
|
-
|
|
748
|
-
|
|
913
|
+
// Run only the stop workflow. RTC cleanup will invoke
|
|
914
|
+
// ScreenshareStoppedCallback to update local, RTM, and layout state.
|
|
915
|
+
const stopped = await executeStopScreenshareWorkflow(
|
|
749
916
|
screenshareAttemptId,
|
|
750
917
|
screenshareSessionId,
|
|
751
918
|
stopOrigin,
|
|
@@ -839,8 +1006,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
839
1006
|
},
|
|
840
1007
|
);
|
|
841
1008
|
logStartRequested();
|
|
842
|
-
|
|
843
|
-
|
|
1009
|
+
// Run only the start workflow. Stop requests received while this is
|
|
1010
|
+
// pending are queued above and executed after startup completes.
|
|
1011
|
+
const started = await executeStartScreenshareWorkflow(
|
|
844
1012
|
screenshareAttemptId,
|
|
845
1013
|
screenshareSessionId,
|
|
846
1014
|
'unknown',
|
|
@@ -891,24 +1059,22 @@ export const ScreenshareConfigure = (props: {
|
|
|
891
1059
|
};
|
|
892
1060
|
stopScreenshareRef.current = stopScreenshare;
|
|
893
1061
|
|
|
894
|
-
const
|
|
895
|
-
isActive: boolean,
|
|
1062
|
+
const executeStartScreenshareWorkflow = async (
|
|
896
1063
|
screenshareAttemptId: string,
|
|
897
1064
|
screenshareSessionId: string,
|
|
898
1065
|
stopOrigin: ScreenshareStopOrigin,
|
|
899
1066
|
stopActorUid?: UidType,
|
|
900
1067
|
) => {
|
|
901
1068
|
const startedAt = Date.now();
|
|
902
|
-
const action: ScreenshareAction = isActive ? 'start' : 'stop';
|
|
903
1069
|
let stage = 'recording_layout';
|
|
904
1070
|
let recordingLayoutFailed = false;
|
|
905
1071
|
let rtcOperationSucceeded = false;
|
|
906
1072
|
logger.log(
|
|
907
1073
|
LogSource.Internals,
|
|
908
1074
|
'SCREENSHARE',
|
|
909
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1075
|
+
`${SCREENSHARE_JOURNEY} screen share start workflow started`,
|
|
910
1076
|
{
|
|
911
|
-
action,
|
|
1077
|
+
action: 'start',
|
|
912
1078
|
stage: 'journey',
|
|
913
1079
|
outcome: 'started',
|
|
914
1080
|
screenshareAttemptId,
|
|
@@ -923,8 +1089,7 @@ export const ScreenshareConfigure = (props: {
|
|
|
923
1089
|
try {
|
|
924
1090
|
if (props.isRecordingActive) {
|
|
925
1091
|
try {
|
|
926
|
-
await
|
|
927
|
-
isActive,
|
|
1092
|
+
await executeStartRecordingLayoutQuery(
|
|
928
1093
|
screenshareAttemptId,
|
|
929
1094
|
screenshareSessionId,
|
|
930
1095
|
stopOrigin,
|
|
@@ -935,10 +1100,10 @@ export const ScreenshareConfigure = (props: {
|
|
|
935
1100
|
logger.error(
|
|
936
1101
|
LogSource.Internals,
|
|
937
1102
|
'SCREENSHARE',
|
|
938
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1103
|
+
`${SCREENSHARE_JOURNEY} screen share start recording layout query failed; continuing screen share start`,
|
|
939
1104
|
recordingError,
|
|
940
1105
|
{
|
|
941
|
-
action,
|
|
1106
|
+
action: 'start',
|
|
942
1107
|
stage,
|
|
943
1108
|
outcome: 'partial_failure',
|
|
944
1109
|
screenshareAttemptId,
|
|
@@ -955,9 +1120,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
955
1120
|
logger.log(
|
|
956
1121
|
LogSource.Internals,
|
|
957
1122
|
'SCREENSHARE',
|
|
958
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1123
|
+
`${SCREENSHARE_JOURNEY} screen share start recording layout query skipped because recording is inactive`,
|
|
959
1124
|
{
|
|
960
|
-
action,
|
|
1125
|
+
action: 'start',
|
|
961
1126
|
stage,
|
|
962
1127
|
outcome: 'skipped',
|
|
963
1128
|
screenshareAttemptId,
|
|
@@ -973,9 +1138,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
973
1138
|
logger.log(
|
|
974
1139
|
LogSource.Internals,
|
|
975
1140
|
'SCREENSHARE',
|
|
976
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1141
|
+
`${SCREENSHARE_JOURNEY} screen share start calling RtcEngine.startScreenshare`,
|
|
977
1142
|
{
|
|
978
|
-
action,
|
|
1143
|
+
action: 'start',
|
|
979
1144
|
stage,
|
|
980
1145
|
outcome: 'started',
|
|
981
1146
|
screenshareAttemptId,
|
|
@@ -987,6 +1152,7 @@ export const ScreenshareConfigure = (props: {
|
|
|
987
1152
|
},
|
|
988
1153
|
);
|
|
989
1154
|
// @ts-ignore
|
|
1155
|
+
// The RTC start API only creates, joins, and publishes screen tracks.
|
|
990
1156
|
await rtc.RtcEngineUnsafe.startScreenshare(
|
|
991
1157
|
screenShareToken,
|
|
992
1158
|
channel,
|
|
@@ -998,7 +1164,6 @@ export const ScreenshareConfigure = (props: {
|
|
|
998
1164
|
{encoderConfig: '1080p_2', optimizationMode: 'detail'},
|
|
999
1165
|
'auto',
|
|
1000
1166
|
{
|
|
1001
|
-
action,
|
|
1002
1167
|
screenshareAttemptId,
|
|
1003
1168
|
screenshareSessionId,
|
|
1004
1169
|
recordingActive: props.isRecordingActive,
|
|
@@ -1011,9 +1176,9 @@ export const ScreenshareConfigure = (props: {
|
|
|
1011
1176
|
logger.log(
|
|
1012
1177
|
LogSource.Internals,
|
|
1013
1178
|
'SCREENSHARE',
|
|
1014
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1179
|
+
`${SCREENSHARE_JOURNEY} screen share start RtcEngine.startScreenshare completed successfully`,
|
|
1015
1180
|
{
|
|
1016
|
-
action,
|
|
1181
|
+
action: 'start',
|
|
1017
1182
|
stage,
|
|
1018
1183
|
outcome: 'success',
|
|
1019
1184
|
screenshareAttemptId,
|
|
@@ -1025,99 +1190,299 @@ export const ScreenshareConfigure = (props: {
|
|
|
1025
1190
|
},
|
|
1026
1191
|
);
|
|
1027
1192
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1193
|
+
activeScreenshareSessionIdRef.current = screenshareSessionId;
|
|
1194
|
+
pendingScreenshareSessionIdRef.current = null;
|
|
1195
|
+
updateOperationState('active');
|
|
1196
|
+
setScreenshareActive(true);
|
|
1197
|
+
stage = 'local_active_state';
|
|
1198
|
+
logger.log(
|
|
1199
|
+
LogSource.Internals,
|
|
1200
|
+
'SCREENSHARE',
|
|
1201
|
+
`${SCREENSHARE_JOURNEY} screen share start local active state set to active`,
|
|
1202
|
+
{
|
|
1203
|
+
action: 'start',
|
|
1204
|
+
stage,
|
|
1205
|
+
outcome: 'success',
|
|
1206
|
+
screenshareAttemptId,
|
|
1207
|
+
screenshareSessionId,
|
|
1208
|
+
recordingActive: props.isRecordingActive,
|
|
1209
|
+
screenShareUid,
|
|
1210
|
+
stopOrigin,
|
|
1211
|
+
stopActorUid,
|
|
1212
|
+
},
|
|
1213
|
+
);
|
|
1214
|
+
stage = 'screenshare_context';
|
|
1215
|
+
setScreenShareData(prevState => {
|
|
1216
|
+
return {
|
|
1217
|
+
...prevState,
|
|
1218
|
+
[screenShareUid]: {
|
|
1219
|
+
name: defaultContentRef.current.defaultContent[screenShareUid]
|
|
1220
|
+
?.name,
|
|
1221
|
+
isActive: true,
|
|
1222
|
+
ts: timeNow(),
|
|
1223
|
+
},
|
|
1224
|
+
};
|
|
1225
|
+
});
|
|
1226
|
+
logger.log(
|
|
1227
|
+
LogSource.Internals,
|
|
1228
|
+
'SCREENSHARE',
|
|
1229
|
+
`${SCREENSHARE_JOURNEY} screen share start local context set to active`,
|
|
1230
|
+
{
|
|
1231
|
+
action: 'start',
|
|
1232
|
+
stage,
|
|
1233
|
+
outcome: 'success',
|
|
1234
|
+
screenshareAttemptId,
|
|
1235
|
+
screenshareSessionId,
|
|
1236
|
+
recordingActive: props.isRecordingActive,
|
|
1237
|
+
screenShareUid,
|
|
1238
|
+
stopOrigin,
|
|
1239
|
+
stopActorUid,
|
|
1240
|
+
},
|
|
1241
|
+
);
|
|
1242
|
+
stage = 'rtm_event';
|
|
1243
|
+
const rtmEventSent = await events.send(
|
|
1244
|
+
EventNames.SCREENSHARE_ATTRIBUTE,
|
|
1245
|
+
JSON.stringify({
|
|
1246
|
+
action: EventActions.SCREENSHARE_STARTED,
|
|
1247
|
+
value: timeNow(),
|
|
1248
|
+
screenUidOfUser: screenShareUid,
|
|
1249
|
+
}),
|
|
1250
|
+
PersistanceLevel.Sender,
|
|
1251
|
+
);
|
|
1252
|
+
logger.log(
|
|
1253
|
+
LogSource.Internals,
|
|
1254
|
+
'SCREENSHARE',
|
|
1255
|
+
`${SCREENSHARE_JOURNEY} screen share start RTM event ${
|
|
1256
|
+
rtmEventSent
|
|
1257
|
+
? 'sent to remote users'
|
|
1258
|
+
: 'failed to send to remote users'
|
|
1259
|
+
}`,
|
|
1260
|
+
{
|
|
1261
|
+
action: 'start',
|
|
1262
|
+
stage,
|
|
1263
|
+
outcome: rtmEventSent ? 'success' : 'failure',
|
|
1264
|
+
screenshareAttemptId,
|
|
1265
|
+
screenshareSessionId,
|
|
1266
|
+
recordingActive: props.isRecordingActive,
|
|
1267
|
+
screenShareUid,
|
|
1268
|
+
stopOrigin,
|
|
1269
|
+
stopActorUid,
|
|
1270
|
+
},
|
|
1271
|
+
);
|
|
1272
|
+
logger.log(
|
|
1273
|
+
LogSource.Internals,
|
|
1274
|
+
'SCREENSHARE',
|
|
1275
|
+
`${SCREENSHARE_JOURNEY} screen share start workflow ${
|
|
1276
|
+
recordingLayoutFailed
|
|
1277
|
+
? 'completed with recording layout failure'
|
|
1278
|
+
: 'completed successfully'
|
|
1279
|
+
}`,
|
|
1280
|
+
{
|
|
1281
|
+
action: 'start',
|
|
1282
|
+
stage: 'complete',
|
|
1283
|
+
outcome: recordingLayoutFailed ? 'partial_success' : 'success',
|
|
1284
|
+
screenshareAttemptId,
|
|
1285
|
+
screenshareSessionId,
|
|
1286
|
+
recordingActive: props.isRecordingActive,
|
|
1287
|
+
screenShareUid,
|
|
1288
|
+
stopOrigin,
|
|
1289
|
+
stopActorUid,
|
|
1290
|
+
elapsedMs: Date.now() - startedAt,
|
|
1291
|
+
},
|
|
1292
|
+
);
|
|
1293
|
+
return true;
|
|
1294
|
+
} catch (e) {
|
|
1295
|
+
const userCancelOrPermissionDenied = isUserCancelOrPermissionDenied(e);
|
|
1296
|
+
logger.error(
|
|
1297
|
+
LogSource.Internals,
|
|
1298
|
+
'SCREENSHARE',
|
|
1299
|
+
`${SCREENSHARE_JOURNEY} screen share start workflow failed at ${stage}: ${
|
|
1300
|
+
userCancelOrPermissionDenied
|
|
1301
|
+
? 'user cancelled picker or permission was denied'
|
|
1302
|
+
: 'unexpected failure'
|
|
1303
|
+
}`,
|
|
1304
|
+
{
|
|
1305
|
+
action: 'start',
|
|
1306
|
+
stage,
|
|
1307
|
+
outcome: userCancelOrPermissionDenied
|
|
1308
|
+
? 'user_cancel_or_permission_denied'
|
|
1309
|
+
: 'failure',
|
|
1310
|
+
screenshareAttemptId,
|
|
1311
|
+
screenshareSessionId,
|
|
1312
|
+
recordingActive: props.isRecordingActive,
|
|
1313
|
+
screenShareUid,
|
|
1314
|
+
stopOrigin,
|
|
1315
|
+
stopActorUid,
|
|
1316
|
+
elapsedMs: Date.now() - startedAt,
|
|
1317
|
+
...getScreenshareError(e),
|
|
1318
|
+
},
|
|
1319
|
+
);
|
|
1320
|
+
Toast.show({
|
|
1321
|
+
leadingIconName: 'alert',
|
|
1322
|
+
type: 'error',
|
|
1323
|
+
text1: toastHeading,
|
|
1324
|
+
text2: toastSubHeading,
|
|
1325
|
+
visibilityTime: 1000 * 10,
|
|
1326
|
+
primaryBtn: null,
|
|
1327
|
+
secondaryBtn: null,
|
|
1328
|
+
});
|
|
1329
|
+
if (!rtcOperationSucceeded) {
|
|
1030
1330
|
pendingScreenshareSessionIdRef.current = null;
|
|
1031
|
-
|
|
1032
|
-
setScreenshareActive(
|
|
1033
|
-
|
|
1331
|
+
activeScreenshareSessionIdRef.current = null;
|
|
1332
|
+
setScreenshareActive(false);
|
|
1333
|
+
updateOperationState('inactive');
|
|
1034
1334
|
logger.log(
|
|
1035
1335
|
LogSource.Internals,
|
|
1036
1336
|
'SCREENSHARE',
|
|
1037
|
-
|
|
1337
|
+
getScreenshareSessionBoundaryMessage('end', screenshareSessionId),
|
|
1038
1338
|
{
|
|
1039
|
-
action,
|
|
1040
|
-
stage,
|
|
1041
|
-
outcome: '
|
|
1339
|
+
action: 'start',
|
|
1340
|
+
stage: 'session_boundary',
|
|
1341
|
+
outcome: 'ended_with_start_failure',
|
|
1042
1342
|
screenshareAttemptId,
|
|
1043
1343
|
screenshareSessionId,
|
|
1044
1344
|
recordingActive: props.isRecordingActive,
|
|
1045
1345
|
screenShareUid,
|
|
1046
1346
|
stopOrigin,
|
|
1047
1347
|
stopActorUid,
|
|
1348
|
+
operationState: operationStateRef.current,
|
|
1048
1349
|
},
|
|
1049
1350
|
);
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1351
|
+
}
|
|
1352
|
+
return rtcOperationSucceeded;
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
const executeStopScreenshareWorkflow = async (
|
|
1357
|
+
screenshareAttemptId: string,
|
|
1358
|
+
screenshareSessionId: string,
|
|
1359
|
+
stopOrigin: ScreenshareStopOrigin,
|
|
1360
|
+
stopActorUid?: UidType,
|
|
1361
|
+
) => {
|
|
1362
|
+
const startedAt = Date.now();
|
|
1363
|
+
let stage = 'recording_layout';
|
|
1364
|
+
let recordingLayoutFailed = false;
|
|
1365
|
+
let rtcOperationSucceeded = false;
|
|
1366
|
+
logger.log(
|
|
1367
|
+
LogSource.Internals,
|
|
1368
|
+
'SCREENSHARE',
|
|
1369
|
+
`${SCREENSHARE_JOURNEY} screen share stop workflow started`,
|
|
1370
|
+
{
|
|
1371
|
+
action: 'stop',
|
|
1372
|
+
stage: 'journey',
|
|
1373
|
+
outcome: 'started',
|
|
1374
|
+
screenshareAttemptId,
|
|
1375
|
+
screenshareSessionId,
|
|
1376
|
+
recordingActive: props.isRecordingActive,
|
|
1377
|
+
screenShareUid,
|
|
1378
|
+
stopOrigin,
|
|
1379
|
+
stopActorUid,
|
|
1380
|
+
channel,
|
|
1381
|
+
},
|
|
1382
|
+
);
|
|
1383
|
+
try {
|
|
1384
|
+
if (props.isRecordingActive) {
|
|
1385
|
+
try {
|
|
1386
|
+
await executeStopRecordingLayoutQuery(
|
|
1071
1387
|
screenshareAttemptId,
|
|
1072
1388
|
screenshareSessionId,
|
|
1073
|
-
recordingActive: props.isRecordingActive,
|
|
1074
|
-
screenShareUid,
|
|
1075
1389
|
stopOrigin,
|
|
1076
1390
|
stopActorUid,
|
|
1077
|
-
|
|
1078
|
-
)
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1391
|
+
);
|
|
1392
|
+
} catch (recordingError) {
|
|
1393
|
+
recordingLayoutFailed = true;
|
|
1394
|
+
logger.error(
|
|
1395
|
+
LogSource.Internals,
|
|
1396
|
+
'SCREENSHARE',
|
|
1397
|
+
`${SCREENSHARE_JOURNEY} screen share stop recording layout query failed; continuing screen share stop`,
|
|
1398
|
+
recordingError,
|
|
1399
|
+
{
|
|
1400
|
+
action: 'stop',
|
|
1401
|
+
stage,
|
|
1402
|
+
outcome: 'partial_failure',
|
|
1403
|
+
screenshareAttemptId,
|
|
1404
|
+
screenshareSessionId,
|
|
1405
|
+
recordingActive: props.isRecordingActive,
|
|
1406
|
+
screenShareUid,
|
|
1407
|
+
stopOrigin,
|
|
1408
|
+
stopActorUid,
|
|
1409
|
+
...getScreenshareError(recordingError),
|
|
1410
|
+
},
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1090
1414
|
logger.log(
|
|
1091
1415
|
LogSource.Internals,
|
|
1092
1416
|
'SCREENSHARE',
|
|
1093
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1094
|
-
rtmEventSent
|
|
1095
|
-
? 'sent to remote users'
|
|
1096
|
-
: 'failed to send to remote users'
|
|
1097
|
-
}`,
|
|
1417
|
+
`${SCREENSHARE_JOURNEY} screen share stop recording layout query skipped because recording is inactive`,
|
|
1098
1418
|
{
|
|
1099
|
-
action,
|
|
1419
|
+
action: 'stop',
|
|
1100
1420
|
stage,
|
|
1101
|
-
outcome:
|
|
1421
|
+
outcome: 'skipped',
|
|
1102
1422
|
screenshareAttemptId,
|
|
1103
1423
|
screenshareSessionId,
|
|
1104
|
-
recordingActive:
|
|
1424
|
+
recordingActive: false,
|
|
1105
1425
|
screenShareUid,
|
|
1106
1426
|
stopOrigin,
|
|
1107
1427
|
stopActorUid,
|
|
1108
1428
|
},
|
|
1109
1429
|
);
|
|
1110
1430
|
}
|
|
1431
|
+
stage = 'rtc_operation';
|
|
1111
1432
|
logger.log(
|
|
1112
1433
|
LogSource.Internals,
|
|
1113
1434
|
'SCREENSHARE',
|
|
1114
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1435
|
+
`${SCREENSHARE_JOURNEY} screen share stop calling RtcEngine.stopScreenshare`,
|
|
1436
|
+
{
|
|
1437
|
+
action: 'stop',
|
|
1438
|
+
stage,
|
|
1439
|
+
outcome: 'started',
|
|
1440
|
+
screenshareAttemptId,
|
|
1441
|
+
screenshareSessionId,
|
|
1442
|
+
recordingActive: props.isRecordingActive,
|
|
1443
|
+
screenShareUid,
|
|
1444
|
+
stopOrigin,
|
|
1445
|
+
stopActorUid,
|
|
1446
|
+
},
|
|
1447
|
+
);
|
|
1448
|
+
// @ts-ignore
|
|
1449
|
+
// The RTC stop API performs idempotent track/client cleanup and then
|
|
1450
|
+
// invokes ScreenshareStoppedCallback for application-state cleanup.
|
|
1451
|
+
await rtc.RtcEngineUnsafe.stopScreenshare({
|
|
1452
|
+
screenshareAttemptId,
|
|
1453
|
+
screenshareSessionId,
|
|
1454
|
+
recordingActive: props.isRecordingActive,
|
|
1455
|
+
screenShareUid,
|
|
1456
|
+
stopOrigin,
|
|
1457
|
+
stopActorUid,
|
|
1458
|
+
});
|
|
1459
|
+
rtcOperationSucceeded = true;
|
|
1460
|
+
logger.log(
|
|
1461
|
+
LogSource.Internals,
|
|
1462
|
+
'SCREENSHARE',
|
|
1463
|
+
`${SCREENSHARE_JOURNEY} screen share stop RtcEngine.stopScreenshare completed successfully`,
|
|
1464
|
+
{
|
|
1465
|
+
action: 'stop',
|
|
1466
|
+
stage,
|
|
1467
|
+
outcome: 'success',
|
|
1468
|
+
screenshareAttemptId,
|
|
1469
|
+
screenshareSessionId,
|
|
1470
|
+
recordingActive: props.isRecordingActive,
|
|
1471
|
+
screenShareUid,
|
|
1472
|
+
stopOrigin,
|
|
1473
|
+
stopActorUid,
|
|
1474
|
+
},
|
|
1475
|
+
);
|
|
1476
|
+
logger.log(
|
|
1477
|
+
LogSource.Internals,
|
|
1478
|
+
'SCREENSHARE',
|
|
1479
|
+
`${SCREENSHARE_JOURNEY} screen share stop workflow ${
|
|
1115
1480
|
recordingLayoutFailed
|
|
1116
1481
|
? 'completed with recording layout failure'
|
|
1117
1482
|
: 'completed successfully'
|
|
1118
1483
|
}`,
|
|
1119
1484
|
{
|
|
1120
|
-
action,
|
|
1485
|
+
action: 'stop',
|
|
1121
1486
|
stage: 'complete',
|
|
1122
1487
|
outcome: recordingLayoutFailed ? 'partial_success' : 'success',
|
|
1123
1488
|
screenshareAttemptId,
|
|
@@ -1135,13 +1500,13 @@ export const ScreenshareConfigure = (props: {
|
|
|
1135
1500
|
logger.error(
|
|
1136
1501
|
LogSource.Internals,
|
|
1137
1502
|
'SCREENSHARE',
|
|
1138
|
-
`${SCREENSHARE_JOURNEY} screen share
|
|
1503
|
+
`${SCREENSHARE_JOURNEY} screen share stop workflow failed at ${stage}: ${
|
|
1139
1504
|
userCancelOrPermissionDenied
|
|
1140
1505
|
? 'user cancelled picker or permission was denied'
|
|
1141
1506
|
: 'unexpected failure'
|
|
1142
1507
|
}`,
|
|
1143
1508
|
{
|
|
1144
|
-
action,
|
|
1509
|
+
action: 'stop',
|
|
1145
1510
|
stage,
|
|
1146
1511
|
outcome: userCancelOrPermissionDenied
|
|
1147
1512
|
? 'user_cancel_or_permission_denied'
|
|
@@ -1165,29 +1530,6 @@ export const ScreenshareConfigure = (props: {
|
|
|
1165
1530
|
primaryBtn: null,
|
|
1166
1531
|
secondaryBtn: null,
|
|
1167
1532
|
});
|
|
1168
|
-
if (isActive && !rtcOperationSucceeded) {
|
|
1169
|
-
pendingScreenshareSessionIdRef.current = null;
|
|
1170
|
-
activeScreenshareSessionIdRef.current = null;
|
|
1171
|
-
setScreenshareActive(false);
|
|
1172
|
-
updateOperationState('inactive');
|
|
1173
|
-
logger.log(
|
|
1174
|
-
LogSource.Internals,
|
|
1175
|
-
'SCREENSHARE',
|
|
1176
|
-
getScreenshareSessionBoundaryMessage('end', screenshareSessionId),
|
|
1177
|
-
{
|
|
1178
|
-
action: 'start',
|
|
1179
|
-
stage: 'session_boundary',
|
|
1180
|
-
outcome: 'ended_with_start_failure',
|
|
1181
|
-
screenshareAttemptId,
|
|
1182
|
-
screenshareSessionId,
|
|
1183
|
-
recordingActive: props.isRecordingActive,
|
|
1184
|
-
screenShareUid,
|
|
1185
|
-
stopOrigin,
|
|
1186
|
-
stopActorUid,
|
|
1187
|
-
operationState: operationStateRef.current,
|
|
1188
|
-
},
|
|
1189
|
-
);
|
|
1190
|
-
}
|
|
1191
1533
|
return rtcOperationSucceeded;
|
|
1192
1534
|
}
|
|
1193
1535
|
};
|