544 lines
24 KiB
TypeScript
544 lines
24 KiB
TypeScript
import { IUserInfo, ISelfInfoParams, IInitParams } from '../interface/ICallService';
|
|
import { CallMediaType, CameraPosition, AudioPlayBackDevice, LOG_LEVEL, devicePermissions } from '@trtc/call-engine-lite-wx';
|
|
import { ICallsParam, IInviteUserParam } from '@trtc/call-engine-lite-wx';
|
|
import { StoreName, CallStatus, NAME, CALL_DATA_KEY, CallRole, COMPONENT, FeatureButton, ButtonState } from '../const/index';
|
|
import { TUICallEngine } from '@trtc/call-engine-lite-wx';
|
|
import { beforeCall, handlePackageError } from './miniProgram';
|
|
import { CallTips, t } from '../locales/index';
|
|
import { handleModalError } from './UIKitModal';
|
|
import { handleRepeatedCallError, performanceNow } from '../utils/common-utils';
|
|
import { getRemoteUserProfile, noDevicePermissionToast, setLocalUserInfoAudioVideoAvailable } from './utils';
|
|
import { ITUIStore } from '../interface/index';
|
|
import TuiStore from '../TUIStore/tuiStore';
|
|
import ChatCombine from './chatCombine';
|
|
import EngineEventHandler from './engineEventHandler';
|
|
const TUIStore: ITUIStore = TuiStore.getInstance();
|
|
const version = '<@VERSION@>';
|
|
export { TUIStore };
|
|
const defaultOfflinePushInfo = { title: '', description: t('you have a new call') };
|
|
|
|
export default class TUICallService {
|
|
static instance: TUICallService;
|
|
public _tuiCallEngine: any;
|
|
private _tim: any = null;
|
|
private _timerId: any = -1;
|
|
private _startTimeStamp: number = performanceNow();
|
|
private _isFromChat: boolean = false;
|
|
private _offlinePushInfo = null;
|
|
private _permissionCheckTimer: any = null;
|
|
private _chatCombine: any = null;
|
|
private _engineEventHandler: any = null;
|
|
|
|
constructor() {
|
|
console.log(`${NAME.PREFIX}version: ${version}`);
|
|
this._watchTUIStore();
|
|
this._engineEventHandler = EngineEventHandler.getInstance({ callService: this });
|
|
|
|
this._chatCombine = ChatCombine.getInstance({ callService: this });
|
|
}
|
|
static getInstance() {
|
|
if (!TUICallService.instance) {
|
|
TUICallService.instance = new TUICallService();
|
|
}
|
|
return TUICallService.instance;
|
|
}
|
|
public async init(params: IInitParams) {
|
|
try {
|
|
if (this._tuiCallEngine) return;
|
|
// @ts-ignore
|
|
let { userID, tim, userSig, sdkAppID, SDKAppID, isFromChat, component = COMPONENT.TUI_CALL_KIT } = params;
|
|
this._tim = tim;
|
|
console.log(`${NAME.PREFIX}init sdkAppId: ${sdkAppID || SDKAppID}, userId: ${userID}`);
|
|
this._tuiCallEngine = TUICallEngine.createInstance({
|
|
tim,
|
|
sdkAppID: sdkAppID || SDKAppID, // 兼容传入 SDKAppID 的问题
|
|
callkitVersion: version,
|
|
isFromChat: isFromChat || false,
|
|
component,
|
|
scene: 'wx-uniapp-vue3-lite',
|
|
});
|
|
this._addListenTuiCallEngineEvent();
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { userId: userID });
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { userId: userID });
|
|
await this._tuiCallEngine.login({ userID, userSig, assetsPath: '' }); // web && mini
|
|
const uiConfig = TUIStore.getData(StoreName.CALL, NAME.CUSTOM_UI_CONFIG);
|
|
this._tuiCallEngine?.reportLog?.({
|
|
name: 'TUICallkit.init',
|
|
data: {
|
|
uiConfig,
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX}init failed, error: ${error}.`);
|
|
throw error;
|
|
}
|
|
}
|
|
// component destroy
|
|
public async destroyed() {
|
|
try {
|
|
const currentCallStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
if (currentCallStatus !== CallStatus.IDLE) {
|
|
throw new Error(`please destroyed when status is idle, current status: ${currentCallStatus}`);
|
|
}
|
|
if (this._tuiCallEngine) {
|
|
this._removeListenTuiCallEngineEvent();
|
|
await this._tuiCallEngine.destroyInstance();
|
|
this._tuiCallEngine = null;
|
|
this._unwatchTUIStore();
|
|
}
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX}destroyed failed, error: ${error}.`);
|
|
throw error;
|
|
}
|
|
}
|
|
// ===============================【通话操作】===============================
|
|
public async calls(callsParams: ICallsParam) {
|
|
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) !== CallStatus.IDLE) return; // avoid double click when application stuck
|
|
try {
|
|
TUIStore.update(StoreName.CALL, NAME.PUSHER_ID, NAME.NEW_PUSHER);
|
|
const { userIDList, type, chatGroupID, offlinePushInfo } = callsParams;
|
|
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) !== CallStatus.IDLE) return;
|
|
const remoteUserInfoList = userIDList.map(userId => ({ userId }));
|
|
await this._updateCallStoreBeforeCall(type, remoteUserInfoList, chatGroupID);
|
|
callsParams.offlinePushInfo = { ...defaultOfflinePushInfo, ...offlinePushInfo };
|
|
const response = await this._tuiCallEngine.calls(callsParams);
|
|
await this._updateCallStoreAfterCall(userIDList, response);
|
|
} catch (error: any) {
|
|
handleModalError(error);
|
|
this._handleCallError(error, 'calls');
|
|
}
|
|
}
|
|
// public async inviteUser(params: IInviteUserParam) {
|
|
// if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) === CallStatus.IDLE) return; // avoid double click when application stuck
|
|
// try {
|
|
// const { userIDList } = params;
|
|
// let inviteUserInfoList = await getRemoteUserProfile(userIDList, this.getTim());
|
|
// const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
|
|
// const userIDListNotInRemoteUserInfoList = userIDList.filter(userId => {
|
|
// return !remoteUserInfoList.some(remoteUserInfo => remoteUserInfo.userId === userId);
|
|
// });
|
|
// if (userIDListNotInRemoteUserInfoList.length === 0) {
|
|
// return;
|
|
// }
|
|
// TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
|
|
// TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
|
|
// this._tuiCallEngine && await this._tuiCallEngine.inviteUser(params);
|
|
// } catch (error: any) {
|
|
// console.error(`${NAME.PREFIX}inviteUser failed, error: ${error}.`);
|
|
// }
|
|
// }
|
|
// public async join(params) {
|
|
// if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) === CallStatus.CONNECTED) return; // avoid double click when application stuck
|
|
// try {
|
|
// const response = await this._tuiCallEngine.join(params);
|
|
// TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
|
|
// this.startTimer();
|
|
|
|
// const updateStoreParams = {
|
|
// [NAME.CALL_ROLE]: CallRole.CALLEE,
|
|
// [NAME.IS_GROUP]: true,
|
|
// [NAME.CALL_STATUS]: CallStatus.CONNECTED,
|
|
// [NAME.CALL_MEDIA_TYPE]: TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE) || CallMediaType.AUDIO, // default audio callMediaType
|
|
// };
|
|
// TUIStore.updateStore(updateStoreParams, StoreName.CALL);
|
|
|
|
// this.setSoundMode(params.type === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
|
|
// const localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
|
|
// TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
|
|
// TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
|
|
|
|
// await this.openMicrophone();
|
|
// setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO);
|
|
// } catch (error) {
|
|
// this._handleCallError(error, 'join');
|
|
// }
|
|
// }
|
|
// ===============================【其它对外接口】===============================
|
|
public getTUICallEngineInstance(): any {
|
|
return this?._tuiCallEngine || null;
|
|
}
|
|
public setLogLevel(level: LOG_LEVEL) {
|
|
this?._tuiCallEngine?.setLogLevel(level);
|
|
}
|
|
public enableFloatWindow(enable: boolean) {
|
|
TUIStore.update(StoreName.CALL, NAME.ENABLE_FLOAT_WINDOW, enable);
|
|
}
|
|
public async setSelfInfo(params: ISelfInfoParams) {
|
|
const { nickName, avatar } = params;
|
|
try {
|
|
await this._tuiCallEngine.setSelfInfo(nickName, avatar);
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX}setSelfInfo failed, error: ${error}.`);
|
|
}
|
|
}
|
|
public async enableMuteMode(enable: boolean) {
|
|
try {
|
|
} catch (error) {
|
|
console.warn(`${NAME.PREFIX}enableMuteMode failed, error: ${error}.`);
|
|
}
|
|
}
|
|
// =============================【实验性接口】=============================
|
|
public callExperimentalAPI(jsonStr: string) {
|
|
const jsonObj = JSON.parse(jsonStr);
|
|
if (jsonObj === jsonStr) return;
|
|
|
|
const { api, params } = jsonObj;
|
|
if (!api || !params) return;
|
|
|
|
try {
|
|
switch(api) {
|
|
case 'forceUseV2API':
|
|
const { enable } = params;
|
|
TUIStore.update(StoreName.CALL, NAME.IS_FORCE_USE_V2_API, !!enable);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (error) {
|
|
this._tuiCallEngine?.reportLog?.({ name: 'TUICallKit.callExperimentalAPI.fail', data: { error } });
|
|
}
|
|
}
|
|
// =============================【内部按钮操作方法】=============================
|
|
public async accept() {
|
|
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
this._tuiCallEngine?.reportLog?.({
|
|
name: 'TUICallKit.accept.start',
|
|
data: { callStatus },
|
|
});
|
|
if (callStatus === CallStatus.CONNECTED) return; // avoid double click when application stuck, especially for miniProgram
|
|
try {
|
|
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
|
|
const deviceMap = {
|
|
microphone: true,
|
|
camera: callMediaType === CallMediaType.VIDEO,
|
|
};
|
|
const currentDevicePermission = await this._tuiCallEngine.deviceCheck(deviceMap);
|
|
if (!currentDevicePermission) {
|
|
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
|
|
if (typeof devicePermissions === 'function') {
|
|
await devicePermissions(callMediaType);
|
|
}
|
|
}
|
|
TUIStore.update(StoreName.CALL, NAME.PUSHER_ID, NAME.NEW_PUSHER);
|
|
const response = await this._tuiCallEngine.accept();
|
|
|
|
await this._handleAcceptResponse(response);
|
|
} catch (error) {
|
|
this._tuiCallEngine?.reportLog?.({
|
|
name: 'TUICallKit.accept.fail',
|
|
level: 'error',
|
|
error,
|
|
});
|
|
if (handleRepeatedCallError(error)) return;
|
|
handleModalError(error);
|
|
noDevicePermissionToast(error, CallMediaType.AUDIO, this._tuiCallEngine);
|
|
this._resetCallStore();
|
|
}
|
|
}
|
|
private async _handleAcceptResponse(response) {
|
|
if (response) {
|
|
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) === CallStatus.CONNECTED) return;
|
|
|
|
// 小程序接通时会进行授权弹框, 状态需要放在 accept 后, 否则先接通后再拉起权限设置
|
|
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, CallStatus.CONNECTED);
|
|
TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
|
|
this.startTimer();
|
|
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
|
|
const isCameraDefaultStateClose = this._getFeatureButtonDefaultState(FeatureButton.Camera) === ButtonState.Close;
|
|
(callMediaType === CallMediaType.VIDEO) && !isCameraDefaultStateClose && await this.openCamera(NAME.LOCAL_VIDEO);
|
|
this.setSoundMode(callMediaType === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
|
|
const localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
|
|
setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO); // web && mini default open audio
|
|
}
|
|
}
|
|
public async hangup() {
|
|
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) === CallStatus.IDLE) return; // avoid double click when application stuck
|
|
await this._tuiCallEngine.hangup();
|
|
this._resetCallStore();
|
|
}
|
|
public async reject() {
|
|
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) === CallStatus.IDLE) return; // avoid double click when application stuck
|
|
|
|
await this._tuiCallEngine.reject();
|
|
this._resetCallStore();
|
|
}
|
|
public async openCamera(videoViewDomID: string) {
|
|
try {
|
|
const currentPosition = TUIStore.getData(StoreName.CALL, NAME.CAMERA_POSITION);
|
|
const isFrontCamera = currentPosition === CameraPosition.FRONT ? true : false;
|
|
this._tuiCallEngine.openCamera(videoViewDomID, isFrontCamera);
|
|
|
|
setLocalUserInfoAudioVideoAvailable(true, NAME.VIDEO);
|
|
} catch (error: any) {
|
|
noDevicePermissionToast(error, CallMediaType.VIDEO, this._tuiCallEngine);
|
|
console.error(`${NAME.PREFIX}openCamera error: ${error}.`);
|
|
}
|
|
}
|
|
public async closeCamera() {
|
|
try {
|
|
await this._tuiCallEngine.closeCamera();
|
|
setLocalUserInfoAudioVideoAvailable(false, NAME.VIDEO);
|
|
} catch (error: any) {
|
|
console.error(`${NAME.PREFIX}closeCamera error: ${error}.`);
|
|
}
|
|
}
|
|
public async openMicrophone() {
|
|
try {
|
|
await this._tuiCallEngine.openMicrophone();
|
|
setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO);
|
|
} catch (error: any) {
|
|
console.error(`${NAME.PREFIX}openMicrophone failed, error: ${error}.`);
|
|
}
|
|
}
|
|
public async closeMicrophone() {
|
|
try {
|
|
await this._tuiCallEngine.closeMicrophone();
|
|
setLocalUserInfoAudioVideoAvailable(false, NAME.AUDIO);
|
|
} catch (error: any) {
|
|
console.error(`${NAME.PREFIX}closeMicrophone failed, error: ${error}.`);
|
|
}
|
|
}
|
|
public switchScreen(userId: string) {
|
|
if(!userId) return;
|
|
TUIStore.update(StoreName.CALL, NAME.BIG_SCREEN_USER_ID, userId);
|
|
}
|
|
public async switchCamera() {
|
|
const currentPosition = TUIStore.getData(StoreName.CALL, NAME.CAMERA_POSITION);
|
|
const targetPosition = currentPosition === CameraPosition.BACK ? CameraPosition.FRONT : CameraPosition.BACK;
|
|
try {
|
|
await this._tuiCallEngine.switchCamera(targetPosition);
|
|
TUIStore.update(StoreName.CALL, NAME.CAMERA_POSITION, targetPosition);
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX}_switchCamera failed, error: ${error}.`);
|
|
}
|
|
}
|
|
public setSoundMode(type?: string): void {
|
|
try {
|
|
let isEarPhone = TUIStore.getData(StoreName.CALL, NAME.IS_EAR_PHONE);
|
|
const soundMode = type || (isEarPhone ? AudioPlayBackDevice.SPEAKER : AudioPlayBackDevice.EAR); // UI 层切换时传参数
|
|
this._tuiCallEngine?.selectAudioPlaybackDevice(soundMode);
|
|
if (type) {
|
|
isEarPhone = type === AudioPlayBackDevice.EAR;
|
|
} else {
|
|
isEarPhone = !isEarPhone;
|
|
}
|
|
TUIStore.update(StoreName.CALL, NAME.IS_EAR_PHONE, isEarPhone);
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX}setSoundMode failed, error: ${error}.`);
|
|
}
|
|
}
|
|
// ==========================【TUICallEngine 事件处理】==========================
|
|
private _addListenTuiCallEngineEvent() {
|
|
this._engineEventHandler.addListenTuiCallEngineEvent();
|
|
}
|
|
private _removeListenTuiCallEngineEvent() {
|
|
this._engineEventHandler.removeListenTuiCallEngineEvent();
|
|
}
|
|
// 处理用户异常退出的情况, 小程序 ”右滑“、"左上角退出"; web 页面关闭浏览器或关闭 tab 页面
|
|
public async handleExceptionExit(event?: any) {
|
|
try {
|
|
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
const callRole = TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE);
|
|
this._tuiCallEngine?.reportLog?.({ name: 'TUICallkit.handleExceptionExit', data: { callStatus, callRole } });
|
|
|
|
if (callStatus === CallStatus.IDLE) return;
|
|
// 在呼叫状态下,被叫调用 reject,主叫调用 hangup
|
|
if (callStatus === CallStatus.CALLING) {
|
|
if (callRole === CallRole.CALLER) {
|
|
await this?.hangup();
|
|
} else {
|
|
await this?.reject();
|
|
}
|
|
}
|
|
if (callStatus === CallStatus.CONNECTED) {
|
|
await this?.hangup();
|
|
}
|
|
this?._resetCallStore();
|
|
} catch (error) {
|
|
console.error(`${NAME.PREFIX} handleExceptionExit failed, error: ${error}.`);
|
|
}
|
|
if (event) {
|
|
event.returnValue = '';
|
|
}
|
|
}
|
|
// 通话时长更新
|
|
public startTimer(): void {
|
|
if (this._timerId === -1) {
|
|
this._startTimeStamp = performanceNow();
|
|
this._timerId = setInterval(() => this._updateCallDuration(), 1000);
|
|
}
|
|
}
|
|
private _stopTimer(): void {
|
|
if (this._timerId !== -1) {
|
|
clearInterval(this._timerId);
|
|
this._timerId = -1;
|
|
}
|
|
}
|
|
// =========================【private methods for service use】=========================
|
|
// 处理 “呼叫” 抛出的异常
|
|
private _handleCallError(error: any, methodName?: string) {
|
|
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
|
|
if (handleRepeatedCallError(error)) return;
|
|
noDevicePermissionToast(error, CallMediaType.AUDIO, this._tuiCallEngine);
|
|
console.error(`${NAME.PREFIX}${methodName} failed, error: ${error}.`);
|
|
this._resetCallStore();
|
|
throw error;
|
|
}
|
|
private async _updateCallStoreBeforeCall(type: number, remoteUserInfoList: IUserInfo[], groupID?: string): Promise<void> {
|
|
let callTips = CallTips.CALLER_CALLING_MSG;
|
|
let updateStoreParams: any = {
|
|
[NAME.CALL_MEDIA_TYPE]: type,
|
|
[NAME.CALL_ROLE]: CallRole.CALLER,
|
|
[NAME.REMOTE_USER_INFO_LIST]: remoteUserInfoList,
|
|
[NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST]: remoteUserInfoList,
|
|
[NAME.IS_GROUP]: (!!groupID || remoteUserInfoList.length > 1),
|
|
[NAME.CALL_TIPS]: callTips,
|
|
[NAME.GROUP_ID]: groupID
|
|
};
|
|
const callStatus = await beforeCall(type, this); // 如果没有权限, 此时为 false. 因此需要在 call 后设置为 calling. 和 web 存在差异
|
|
console.log(`${NAME.PREFIX}mini beforeCall return callStatus: ${callStatus}.`);
|
|
TUIStore.updateStore({ ...updateStoreParams, [NAME.CALL_STATUS]: callStatus }, StoreName.CALL);
|
|
const remoteUserInfoLists = await getRemoteUserProfile(remoteUserInfoList.map(obj => obj.userId), this.getTim());
|
|
|
|
if (remoteUserInfoLists.length > 0) {
|
|
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, remoteUserInfoLists);
|
|
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoLists);
|
|
}
|
|
|
|
const deviceMap = {
|
|
microphone: true,
|
|
camera: type === CallMediaType.VIDEO,
|
|
};
|
|
let hasDevicePermission = await this._tuiCallEngine.deviceCheck(deviceMap);
|
|
if (!hasDevicePermission) {
|
|
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
|
|
this._permissionCheckTimer = setInterval(async () => {
|
|
hasDevicePermission = await this._tuiCallEngine.deviceCheck(deviceMap);
|
|
|
|
if (hasDevicePermission && this._permissionCheckTimer) {
|
|
clearInterval(this._permissionCheckTimer);
|
|
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, CallStatus.CALLING);
|
|
}
|
|
}, 500);
|
|
}
|
|
}
|
|
private async _updateCallStoreAfterCall(userIdList: string[], response: any) {
|
|
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
|
|
let localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
|
|
TUIStore.update(StoreName.CALL, NAME.CALL_INFO, { mediaType: callMediaType, inviterId: localUserInfo.userId });
|
|
if (response) {
|
|
TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
|
|
this.setSoundMode(callMediaType === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
|
|
|
|
const isCameraDefaultStateClose = this._getFeatureButtonDefaultState(FeatureButton.Camera) === ButtonState.Close;
|
|
if((callMediaType === CallMediaType.VIDEO) && !isCameraDefaultStateClose) {
|
|
await this.openCamera(NAME.LOCAL_VIDEO);
|
|
}
|
|
localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
|
|
setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO); // web && mini, default open audio
|
|
} else {
|
|
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
|
|
this._permissionCheckTimer = null;
|
|
this._resetCallStore();
|
|
}
|
|
}
|
|
private _getFeatureButtonDefaultState(buttonName: FeatureButton) {
|
|
const { button: buttonConfig } = TUIStore.getData(StoreName.CALL, NAME.CUSTOM_UI_CONFIG);
|
|
return buttonConfig?.[buttonName]?.state;
|
|
}
|
|
private _updateCallDuration(): void {
|
|
TUIStore.update(StoreName.CALL, NAME.DURATION, Math.round((performanceNow() - this._startTimeStamp) / 1000));
|
|
}
|
|
private _resetCallStore() {
|
|
this._stopTimer();
|
|
// localUserInfo, language 在通话结束后不需要清除
|
|
// callStatus 清除需要通知; isMinimized 也需要通知(basic-vue3 中切小窗关闭后, 再呼叫还是小窗, 因此需要通知到组件侧)
|
|
// isGroup 也不清除(engine 先抛 cancel 事件, 再抛 reject 事件)
|
|
// displayMode、videoResolution 也不能清除, 组件不卸载, 这些属性也需保留, 否则采用默认值.
|
|
// enableFloatWindow 不清除:开启/关闭悬浮窗功能。
|
|
let notResetOrNotifyKeys = Object.keys(CALL_DATA_KEY).filter((key) => {
|
|
switch (CALL_DATA_KEY[key]) {
|
|
case NAME.CALL_STATUS:
|
|
case NAME.LANGUAGE:
|
|
case NAME.IS_GROUP:
|
|
case NAME.ENABLE_FLOAT_WINDOW:
|
|
case NAME.LOCAL_USER_INFO:
|
|
case NAME.IS_FORCE_USE_V2_API:
|
|
case NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN: {
|
|
return false;
|
|
}
|
|
default: {
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
notResetOrNotifyKeys = notResetOrNotifyKeys.map(key => CALL_DATA_KEY[key]);
|
|
TUIStore.reset(StoreName.CALL, notResetOrNotifyKeys);
|
|
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
|
|
callStatus !== CallStatus.IDLE && TUIStore.reset(StoreName.CALL, [NAME.CALL_STATUS], true); // callStatus reset need notify
|
|
TUIStore.reset(StoreName.CALL, [NAME.IS_MINIMIZED], true); // isMinimized reset need notify
|
|
TUIStore.reset(StoreName.CALL, [NAME.IS_EAR_PHONE], true); // isEarPhone reset need notify
|
|
TUIStore.reset(StoreName.CALL, [NAME.PUSHER_ID], true); // pusher unload reset need notify
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, {
|
|
...TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO),
|
|
isVideoAvailable: false,
|
|
isAudioAvailable: false,
|
|
});
|
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, {
|
|
...TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN),
|
|
isVideoAvailable: false,
|
|
isAudioAvailable: false,
|
|
});
|
|
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, []);
|
|
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, []);
|
|
TUIStore.update(StoreName.CALL, NAME.CAMERA_POSITION, CameraPosition.FRONT);
|
|
TUIStore.update(StoreName.CALL, NAME.CALL_INFO, {});
|
|
}
|
|
// =========================【监听 TUIStore 中的状态及处理】=========================
|
|
private _handleCallStatusChange = async (value: CallStatus) => {
|
|
try {
|
|
if (value === CallStatus.CALLING) {
|
|
} else {
|
|
// 状态变更通知
|
|
if (value === CallStatus.CONNECTED) {
|
|
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
|
|
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
|
|
const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
|
|
TUIStore.update(StoreName.CALL, NAME.CALL_TIPS, '');
|
|
if (!isGroup && callMediaType === CallMediaType.VIDEO) {
|
|
this.switchScreen(remoteUserInfoList[0].domId);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.warn(`${NAME.PREFIX}handleCallStatusChange, ${error}.`);
|
|
}
|
|
};
|
|
private _watchTUIStore() {
|
|
TUIStore?.watch(StoreName.CALL, {
|
|
[NAME.CALL_STATUS]: this._handleCallStatusChange,
|
|
});
|
|
}
|
|
private _unwatchTUIStore() {
|
|
TUIStore?.unwatch(StoreName.CALL, {
|
|
[NAME.CALL_STATUS]: this._handleCallStatusChange,
|
|
});
|
|
}
|
|
// =========================【set、get methods】=========================
|
|
public getTim() {
|
|
if (this._tim) return this._tim;
|
|
if (!this._tuiCallEngine) {
|
|
console.warn(`${NAME.PREFIX}getTim warning: _tuiCallEngine Instance is not available.`);
|
|
return null;
|
|
}
|
|
return this._tuiCallEngine?.tim || this._tuiCallEngine?.getTim(); // mini support getTim interface
|
|
}
|
|
public setIsFromChat(isFromChat: boolean) {
|
|
this._isFromChat = isFromChat;
|
|
}
|
|
}
|