@trtc/calls-uikit-vue2 4.4.5 → 4.4.7
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 +3 -3
- package/src/Components/hooks/useCustomUIButtonConfig.ts +37 -2
- package/src/TUICallService/CallService/index.ts +15 -6
- package/src/TUICallService/serve/callManager.ts +6 -2
- package/src/TUICallService/utils/common-utils.ts +156 -11
- package/src/index.ts +1 -1
- package/tuicall-uikit-vue2.es.js +2457 -2404
- package/tuicall-uikit-vue2.umd.js +2 -2
- package/types/TUICallService/CallService/engineEventHandler.d.ts +1 -0
- package/types/TUICallService/utils/common-utils.d.ts +9 -0
- package/types/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trtc/calls-uikit-vue2",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.7",
|
|
4
4
|
"main": "./tuicall-uikit-vue2.umd.js",
|
|
5
5
|
"module": "./tuicall-uikit-vue2.es.js",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@tencentcloud/tui-core-lite": "1.0.0",
|
|
17
|
-
"@trtc/call-engine-lite-js": "~3.5.
|
|
17
|
+
"@trtc/call-engine-lite-js": "~3.5.8",
|
|
18
18
|
"@tencentcloud/lite-chat": "^1.6.3",
|
|
19
|
-
"@trtc/call-engine-lite-wx": "~3.4.
|
|
19
|
+
"@trtc/call-engine-lite-wx": "~3.4.8"
|
|
20
20
|
},
|
|
21
21
|
"bugs": {
|
|
22
22
|
"url": "https://github.com/tencentyun/TUICallKit/issues"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { watch, ref, toRefs, computed } from '../../adapter-vue';
|
|
2
2
|
import { useCustomUI } from './useCustomUI';
|
|
3
|
-
import { TUIGlobal, CallStatus } from '../../TUICallService';
|
|
3
|
+
import { TUIGlobal, CallStatus, TUIStore, StoreName, NAME } from '../../TUICallService';
|
|
4
4
|
import { add, deepClone, findValues, modify } from '../util';
|
|
5
5
|
import { VirtualBackgroundMobileConfig } from '../components/common/ButtonPanel/config/VirtualBackgroundMobileConfig';
|
|
6
6
|
import { useCallInfoContext } from './useCallInfoContext';
|
|
@@ -36,15 +36,46 @@ function setCloseCameraConfig(config, isVideoAvailable, isShowVirtualBackgroundI
|
|
|
36
36
|
return newConfig;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Hide inviteUser button when groupID is empty (not a Chat group call)
|
|
41
|
+
* This prevents showing the invite button when there's no group member list available
|
|
42
|
+
*/
|
|
43
|
+
function setInviteUserVisibility(config, groupID) {
|
|
44
|
+
const newConfig = deepClone(config);
|
|
45
|
+
const hasValidGroupID = groupID && typeof groupID === 'string' && groupID.trim() !== '';
|
|
46
|
+
|
|
47
|
+
if (!hasValidGroupID) {
|
|
48
|
+
// Hide inviteUser button in all group call scenarios when no valid groupID
|
|
49
|
+
// PC group call video
|
|
50
|
+
modify(newConfig, 'pc.groupCall.video.calling[0][2].props.show', false);
|
|
51
|
+
modify(newConfig, 'pc.groupCall.video.connected[0][3].props.show', false);
|
|
52
|
+
// PC group call audio
|
|
53
|
+
modify(newConfig, 'pc.groupCall.audio.calling[0][2].props.show', false);
|
|
54
|
+
modify(newConfig, 'pc.groupCall.audio.connected[0][3].props.show', false);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return newConfig;
|
|
58
|
+
}
|
|
59
|
+
|
|
39
60
|
export function useCustomUIButtonConfig() {
|
|
40
61
|
const { isShowEnableVirtualBackground, callStatus } = toRefs(useCallInfoContext());
|
|
41
62
|
const customUIConfig = useCustomUI();
|
|
42
63
|
const { localUserInfoExcludeVolume: localUserInfo } = toRefs(useUserInfoExcludeVolumeContext());
|
|
43
64
|
const isVideoAvailable = computed(() => localUserInfo?.value.isVideoAvailable || false);
|
|
44
65
|
const isShowVirtualBackgroundIcon = computed(() => isShowEnableVirtualBackground.value && !TUIGlobal.isH5);
|
|
66
|
+
const groupID = ref(TUIStore.getData(StoreName.CALL, NAME.GROUP_ID));
|
|
45
67
|
const results = ref([]);
|
|
46
68
|
|
|
47
|
-
|
|
69
|
+
// Watch groupID changes from TUIStore
|
|
70
|
+
const handleGroupIDChange = (value) => {
|
|
71
|
+
groupID.value = value;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
TUIStore.watch(StoreName.CALL, {
|
|
75
|
+
[NAME.GROUP_ID]: handleGroupIDChange,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
watch([customUIConfig, isShowEnableVirtualBackground, isVideoAvailable, groupID], () => {
|
|
48
79
|
let initConfig = deepClone(ButtonPanelConfig);
|
|
49
80
|
if (isShowVirtualBackgroundIcon.value) {
|
|
50
81
|
initConfig = setVirtualBackgroundConfig(ButtonPanelConfig);
|
|
@@ -52,6 +83,10 @@ export function useCustomUIButtonConfig() {
|
|
|
52
83
|
if(callStatus.value === CallStatus.CONNECTED) {
|
|
53
84
|
initConfig = setCloseCameraConfig(initConfig, isVideoAvailable.value, isShowVirtualBackgroundIcon.value);
|
|
54
85
|
}
|
|
86
|
+
|
|
87
|
+
// Hide inviteUser button when not in a Chat group (no groupID)
|
|
88
|
+
initConfig = setInviteUserVisibility(initConfig, groupID.value);
|
|
89
|
+
|
|
55
90
|
const { button: buttonsConfig } = customUIConfig.value;
|
|
56
91
|
const rs = [];
|
|
57
92
|
function condition(value) {
|
|
@@ -27,7 +27,7 @@ const TUIGlobal: ITUIGlobal = TuiGlobal.getInstance();
|
|
|
27
27
|
const TUIStore: ITUIStore = TuiStore.getInstance();
|
|
28
28
|
const uiDesign = UIDesign.getInstance();
|
|
29
29
|
uiDesign.setTUIStore(TUIStore);
|
|
30
|
-
const version = '4.4.
|
|
30
|
+
const version = '4.4.7';
|
|
31
31
|
import AIAssistant from './AIAssistant'; // 仅 web 支持 AI 实时字幕
|
|
32
32
|
const frameWork = 'vue2.7';
|
|
33
33
|
export { TUIGlobal, TUIStore, uiDesign };
|
|
@@ -321,7 +321,6 @@ export default class TUICallService {
|
|
|
321
321
|
public setCameraDefaultState(isOpen: boolean) {
|
|
322
322
|
uiDesign.setCameraDefaultState(isOpen);
|
|
323
323
|
}
|
|
324
|
-
// AI Subtitle functionality removed, focusing on real-time transcriber
|
|
325
324
|
// =============================【实验性接口】=============================
|
|
326
325
|
public callExperimentalAPI(jsonStr: string) {
|
|
327
326
|
const jsonObj = JSON.parse(jsonStr);
|
|
@@ -332,6 +331,9 @@ export default class TUICallService {
|
|
|
332
331
|
|
|
333
332
|
try {
|
|
334
333
|
switch(api) {
|
|
334
|
+
case 'setAssetsPath':
|
|
335
|
+
TUICallEngine?.callExperimentalAPI?.(jsonStr);
|
|
336
|
+
break;
|
|
335
337
|
case 'forceUseV2API':
|
|
336
338
|
const { enable } = params;
|
|
337
339
|
TUIStore.update(StoreName.CALL, NAME.IS_FORCE_USE_V2_API, !!enable);
|
|
@@ -579,15 +581,22 @@ export default class TUICallService {
|
|
|
579
581
|
public executeExternalAfterCalling(): void {
|
|
580
582
|
this.afterCalling && this.afterCalling();
|
|
581
583
|
}
|
|
582
|
-
//
|
|
584
|
+
// Handle exception exit: mini program "swipe right", "exit from top left"; web close browser or close tab
|
|
583
585
|
public async handleExceptionExit(event?: any) {
|
|
584
586
|
try {
|
|
585
587
|
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
586
588
|
const callRole = TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE);
|
|
587
|
-
|
|
589
|
+
|
|
590
|
+
this._tuiCallEngine?.reportLog?.({
|
|
591
|
+
name: 'TUICallkit.handleExceptionExit',
|
|
592
|
+
data: { callStatus, callRole, source: event?.type || 'unknown' }
|
|
593
|
+
});
|
|
588
594
|
|
|
589
595
|
if (callStatus === CallStatus.IDLE) return;
|
|
590
|
-
|
|
596
|
+
|
|
597
|
+
console.log(`${NAME.PREFIX} handleExceptionExit triggered, status: ${callStatus}, role: ${callRole}, event: ${event?.type}`);
|
|
598
|
+
|
|
599
|
+
// Execute hangup/reject based on call status and role
|
|
591
600
|
if (callStatus === CallStatus.CALLING) {
|
|
592
601
|
if (callRole === CallRole.CALLER) {
|
|
593
602
|
await this?.hangup();
|
|
@@ -627,7 +636,7 @@ export default class TUICallService {
|
|
|
627
636
|
}
|
|
628
637
|
}
|
|
629
638
|
// =========================【private methods for service use】=========================
|
|
630
|
-
// 处理
|
|
639
|
+
// 处理 "呼叫" 抛出的异常
|
|
631
640
|
private _handleCallError(error: any, methodName?: string) {
|
|
632
641
|
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
|
|
633
642
|
|
|
@@ -72,11 +72,15 @@ export class CallManager {
|
|
|
72
72
|
// @ts-ignore
|
|
73
73
|
wx.navigateTo({
|
|
74
74
|
url: `/${this._globalCallPagePath}`,
|
|
75
|
-
success: () => {
|
|
75
|
+
success: () => {
|
|
76
|
+
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
77
|
+
if (callStatus === CallStatus.IDLE) {
|
|
78
|
+
this._handleCallStatusToIdle();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
76
81
|
fail: () => {
|
|
77
82
|
console.error(`${PREFIX} navigateTo fail!`);
|
|
78
83
|
},
|
|
79
|
-
complete: () => {},
|
|
80
84
|
});
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -252,18 +252,163 @@ export function interpolate(str, data) {
|
|
|
252
252
|
return data[key] !== undefined ? String(data[key]) : match;
|
|
253
253
|
});
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Detect current runtime environment
|
|
258
|
+
*/
|
|
259
|
+
function detectEnvironment(): 'pc-browser' | 'mobile-browser' | 'webview' | 'miniprogram' {
|
|
260
|
+
const ua = navigator.userAgent || '';
|
|
261
|
+
const uaLower = ua.toLowerCase();
|
|
262
|
+
|
|
263
|
+
// WeChat MiniProgram WebView
|
|
264
|
+
if (uaLower.includes('miniprogram') ||
|
|
265
|
+
(window as any).__wxjs_environment === 'miniprogram') {
|
|
266
|
+
return 'miniprogram';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// App WebView
|
|
270
|
+
if (uaLower.includes('webview') ||
|
|
271
|
+
uaLower.includes(' wv') ||
|
|
272
|
+
(window as any).webkit?.messageHandlers) {
|
|
273
|
+
return 'webview';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Mobile browser
|
|
277
|
+
if (/Android|iPhone|iPad|iPod|Mobile/i.test(ua)) {
|
|
278
|
+
return 'mobile-browser';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return 'pc-browser';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Detect if current device is iOS
|
|
286
|
+
*/
|
|
287
|
+
function isIOS(): boolean {
|
|
288
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent || '');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Detect if current device is Android
|
|
293
|
+
*/
|
|
294
|
+
function isAndroid(): boolean {
|
|
295
|
+
return /Android/.test(navigator.userAgent || '');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Enhanced browser/webview close detection for exception exit handling
|
|
300
|
+
* Supports: PC browsers (Chrome/Safari/Firefox/Edge), Mobile browsers (Android/iOS), WebView
|
|
301
|
+
*
|
|
302
|
+
* Note: Mobile background switching (visibilitychange to hidden) will NOT trigger hangup,
|
|
303
|
+
* only actual page close/navigation events will trigger the callback.
|
|
304
|
+
*
|
|
305
|
+
* @param callback - Function to call when exit is detected
|
|
306
|
+
*/
|
|
256
307
|
export function initBrowserCloseDetection(callback: Function) {
|
|
257
|
-
if (window
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
308
|
+
if (typeof window === 'undefined') return;
|
|
309
|
+
|
|
310
|
+
let isExiting = false;
|
|
311
|
+
const env = detectEnvironment();
|
|
312
|
+
|
|
313
|
+
console.log(`[ExitDetection] Environment: ${env}, iOS: ${isIOS()}, Android: ${isAndroid()}`);
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Trigger exit handler with deduplication
|
|
317
|
+
*/
|
|
318
|
+
const triggerExit = (source: string, event?: Event) => {
|
|
319
|
+
if (isExiting) return;
|
|
320
|
+
isExiting = true;
|
|
321
|
+
|
|
322
|
+
console.log(`[ExitDetection] Exit triggered by: ${source}`);
|
|
323
|
+
|
|
324
|
+
try {
|
|
266
325
|
callback(event);
|
|
267
|
-
})
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(`[ExitDetection] Callback error:`, error);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Reset flag after delay (for bfcache scenarios)
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
isExiting = false;
|
|
333
|
+
}, 1000);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Handle beforeunload event
|
|
338
|
+
* Best for: PC browsers, Android browsers
|
|
339
|
+
*/
|
|
340
|
+
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
341
|
+
// Skip if it's just navigation (not actual page close)
|
|
342
|
+
const navigationEntries = performance?.getEntriesByType?.('navigation') || [];
|
|
343
|
+
const navigationEntry = navigationEntries[0] as PerformanceNavigationTiming;
|
|
344
|
+
if (navigationEntry && navigationEntry.type === 'navigate') {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
triggerExit('beforeunload', event);
|
|
349
|
+
|
|
350
|
+
// Required for some browsers to trigger the event
|
|
351
|
+
event.returnValue = '';
|
|
352
|
+
return '';
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Handle pagehide event
|
|
357
|
+
* Best for: iOS Safari, all modern browsers
|
|
358
|
+
* More reliable than beforeunload on iOS
|
|
359
|
+
*/
|
|
360
|
+
const handlePageHide = (event: PageTransitionEvent) => {
|
|
361
|
+
// persisted=true means page might be restored from bfcache
|
|
362
|
+
if (!event.persisted) {
|
|
363
|
+
triggerExit('pagehide', event);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Handle visibilitychange event
|
|
369
|
+
* Note: We only log this event, do NOT trigger hangup on mobile background switch
|
|
370
|
+
* Because user may just switch apps temporarily and come back
|
|
371
|
+
*/
|
|
372
|
+
const handleVisibilityChange = () => {
|
|
373
|
+
if (document.visibilityState === 'hidden') {
|
|
374
|
+
// Only log, do NOT trigger exit on visibility change
|
|
375
|
+
// Mobile users often switch apps temporarily during calls
|
|
376
|
+
console.log(`[ExitDetection] Visibility changed to hidden (not triggering exit)`);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Handle freeze event (Page Lifecycle API, Chrome 68+)
|
|
382
|
+
* This indicates the page is being frozen, which is a strong signal of exit
|
|
383
|
+
*/
|
|
384
|
+
const handleFreeze = () => {
|
|
385
|
+
triggerExit('freeze');
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// ============ Register event listeners based on environment ============
|
|
389
|
+
|
|
390
|
+
// 1. pagehide - Most reliable for iOS, works on all modern browsers
|
|
391
|
+
if (window.addEventListener) {
|
|
392
|
+
window.addEventListener('pagehide', handlePageHide, { capture: true });
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// 2. beforeunload - Best for PC browsers and Android
|
|
396
|
+
if (env === 'pc-browser' || isAndroid()) {
|
|
397
|
+
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
268
398
|
}
|
|
399
|
+
|
|
400
|
+
// 3. visibilitychange - Only for logging, not triggering exit
|
|
401
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
402
|
+
|
|
403
|
+
// 4. freeze - Page Lifecycle API (Chrome 68+)
|
|
404
|
+
if ('onfreeze' in document) {
|
|
405
|
+
document.addEventListener('freeze', handleFreeze as EventListener);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// 5. unload - Legacy fallback (deprecated but still useful in some cases)
|
|
409
|
+
window.addEventListener('unload', (event) => {
|
|
410
|
+
triggerExit('unload', event);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
console.log(`[ExitDetection] Listeners registered for environment: ${env}`);
|
|
269
414
|
}
|