@tencentcloud/call-uikit-wx
Version:
An Open-source Voice & Video Calling UI Component Based on Tencent Cloud Service.
891 lines • 59.2 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.uiDesign = exports.TUIStore = exports.TUIGlobal = void 0;
const index_1 = require("../const/index");
// @ts-ignore
const call_engine_wx_1 = require("@tencentcloud/call-engine-wx");
const miniProgram_1 = require("./miniProgram");
const index_2 = require("../locales/index");
const bellContext_1 = require("./bellContext");
const index_3 = require("../utils/validate/index");
const common_utils_1 = require("../utils/common-utils");
const utils_1 = require("./utils");
const timer_1 = __importDefault(require("../utils/timer"));
const tuiGlobal_1 = __importDefault(require("../TUIGlobal/tuiGlobal"));
const tuiStore_1 = __importDefault(require("../TUIStore/tuiStore"));
const UIDesign_1 = require("./UIDesign");
const chatCombine_1 = __importDefault(require("./chatCombine"));
const engineEventHandler_1 = __importDefault(require("./engineEventHandler"));
const TUIGlobal = tuiGlobal_1.default.getInstance();
exports.TUIGlobal = TUIGlobal;
const TUIStore = tuiStore_1.default.getInstance();
exports.TUIStore = TUIStore;
const uiDesign = UIDesign_1.UIDesign.getInstance();
exports.uiDesign = uiDesign;
uiDesign.setTUIStore(TUIStore);
const version = '4.0.9';
class TUICallService {
constructor() {
this._tim = null;
this._TUICore = null;
this._timerId = -1;
this._startTimeStamp = (0, common_utils_1.performanceNow)();
this._bellContext = null;
this._isFromChat = false;
this._currentGroupId = ''; // The currentGroupId of the group chat that the user is currently in
this._preDevicePermission = false; // Record mini program device permissions
this._offlinePushInfo = null;
this._permissionCheckTimer = null;
this._chatCombine = null;
this._engineEventHandler = null;
// =========================【监听 TUIStore 中的状态及处理】=========================
this._handleCallStatusChange = (value) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
const bellParams = {
callRole: TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_ROLE),
callStatus: TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS),
};
this._bellContext.setBellProperties(bellParams);
if (value === index_1.CallStatus.CALLING) {
yield ((_a = this === null || this === void 0 ? void 0 : this._bellContext) === null || _a === void 0 ? void 0 : _a.play());
}
else {
// 状态变更通知
if (value === index_1.CallStatus.CONNECTED) {
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
const remoteUserInfoList = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST);
const oldStatus = isGroup ? index_1.StatusChange.DIALING_GROUP : index_1.StatusChange.DIALING_C2C;
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_TIPS, '');
this.statusChanged && this.statusChanged({ oldStatus, newStatus: (0, utils_1.generateStatusChangeText)() });
if (!isGroup && callMediaType === index_1.CallMediaType.VIDEO) {
this.switchScreen(remoteUserInfoList[0].domId);
}
}
yield ((_b = this === null || this === void 0 ? void 0 : this._bellContext) === null || _b === void 0 ? void 0 : _b.stop());
}
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}handleCallStatusChange, ${error}.`);
}
});
console.log(`${index_1.NAME.PREFIX}version: ${version}`);
this._watchTUIStore();
this._engineEventHandler = engineEventHandler_1.default.getInstance({ callService: this });
this._chatCombine = chatCombine_1.default.getInstance({ callService: this });
}
static getInstance() {
if (!TUICallService.instance) {
TUICallService.instance = new TUICallService();
}
return TUICallService.instance;
}
init(params) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
try {
if (this._tuiCallEngine)
return;
// @ts-ignore
let { userID, tim, userSig, sdkAppID, SDKAppID, isFromChat, component = index_1.COMPONENT.TUI_CALL_KIT } = params;
if (this._TUICore) {
sdkAppID = this._TUICore.SDKAppID;
tim = this._TUICore.tim;
}
this._tim = tim;
console.log(`${index_1.NAME.PREFIX}init sdkAppId: ${sdkAppID || SDKAppID}, userId: ${userID}`);
this._tuiCallEngine = call_engine_wx_1.TUICallEngine.createInstance({
tim,
// @ts-ignore
sdkAppID: sdkAppID || SDKAppID,
callkitVersion: version,
isFromChat: isFromChat || false,
component,
});
uiDesign.setEngineInstance(this._tuiCallEngine);
this._addListenTuiCallEngineEvent();
this._bellContext = new bellContext_1.BellContext();
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, { userId: userID });
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { userId: userID });
uiDesign.updateViewBackgroundUserId('local');
yield this._tuiCallEngine.login({ userID, userSig, assetsPath: '' }); // web && mini
const uiConfig = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CUSTOM_UI_CONFIG);
(_b = (_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.reportLog) === null || _b === void 0 ? void 0 : _b.call(_a, {
name: 'TUICallkit.init',
data: {
uiConfig,
}
});
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}init failed, error: ${error}.`);
throw error;
}
});
}
// component destroy
destroyed() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
const currentCallStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
if (currentCallStatus !== index_1.CallStatus.IDLE) {
throw new Error(`please destroyed when status is idle, current status: ${currentCallStatus}`);
}
if (this._tuiCallEngine) {
this._removeListenTuiCallEngineEvent();
yield this._tuiCallEngine.destroyInstance();
this._tuiCallEngine = null;
}
(_a = this._bellContext) === null || _a === void 0 ? void 0 : _a.destroy();
this._bellContext = null;
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}destroyed failed, error: ${error}.`);
throw error;
}
});
}
// ===============================【通话操作】===============================
call(callParams) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const { type, userID, offlinePushInfo } = callParams;
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return;
yield this._updateCallStoreBeforeCall(type, [{ userId: userID }]);
this.executeExternalBeforeCalling(); // 执行外部传入的 beforeCall 方法
callParams.offlinePushInfo = Object.assign(Object.assign({}, this.getDefaultOfflinePushInfo()), offlinePushInfo);
const response = yield this._tuiCallEngine.call(callParams);
yield this._updateCallStoreAfterCall([userID], response);
}
catch (error) {
this._handleCallError(error, 'call');
}
});
}
;
groupCall(groupCallParams) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const { userIDList, type, groupID, offlinePushInfo } = groupCallParams;
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return;
const remoteUserInfoList = userIDList.map(userId => ({ userId }));
yield this._updateCallStoreBeforeCall(type, remoteUserInfoList, groupID);
this.executeExternalBeforeCalling();
groupCallParams.offlinePushInfo = Object.assign(Object.assign({}, this.getDefaultOfflinePushInfo()), offlinePushInfo);
const response = yield this._tuiCallEngine.groupCall(groupCallParams);
yield this._updateCallStoreAfterCall(userIDList, response);
}
catch (error) {
this._handleCallError(error, 'groupCall');
}
});
}
inviteUser(params) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) === index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const { userIDList } = params;
let inviteUserInfoList = yield (0, utils_1.getRemoteUserProfile)(userIDList, this.getTim());
const remoteUserInfoList = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST);
const userIDListNotInRemoteUserInfoList = userIDList.filter(userId => {
return !remoteUserInfoList.some(remoteUserInfo => remoteUserInfo.userId === userId);
});
if (userIDListNotInRemoteUserInfoList.length === 0) {
return;
}
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
this._tuiCallEngine && (yield this._tuiCallEngine.inviteUser(params));
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}inviteUser failed, error: ${error}.`);
}
});
}
joinInGroupCall(params) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) === index_1.CallStatus.CONNECTED)
return; // avoid double click when application stuck
try {
const updateStoreParams = {
[index_1.NAME.CALL_ROLE]: index_1.CallRole.CALLEE,
[index_1.NAME.IS_GROUP]: true,
[index_1.NAME.CALL_STATUS]: index_1.CallStatus.CONNECTED,
[index_1.NAME.CALL_MEDIA_TYPE]: params.type,
[index_1.NAME.GROUP_ID]: params.groupID,
[index_1.NAME.ROOM_ID]: params.roomID,
};
TUIStore.updateStore(updateStoreParams, index_1.StoreName.CALL);
const response = yield this._tuiCallEngine.joinInGroupCall(params);
const isCameraDefaultStateClose = this._getFeatureButtonDefaultState(index_1.FeatureButton.Camera) === index_1.ButtonState.Close;
(params.type === index_1.CallMediaType.VIDEO) && !isCameraDefaultStateClose && (yield this.openCamera(index_1.NAME.LOCAL_VIDEO));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_CLICKABLE, true);
this.startTimer();
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.PUSHER, response);
this.setSoundMode(params.type === index_1.CallMediaType.AUDIO ? index_1.AudioPlayBackDevice.EAR : index_1.AudioPlayBackDevice.SPEAKER);
const localUserInfo = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.AUDIO);
}
catch (error) {
this._handleCallError(error, 'joinInGroupCall');
}
});
}
calls(callsParams) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const { userIDList, type, chatGroupID, offlinePushInfo } = callsParams;
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) !== index_1.CallStatus.IDLE)
return;
const remoteUserInfoList = userIDList.map(userId => ({ userId }));
yield this._updateCallStoreBeforeCall(type, remoteUserInfoList, chatGroupID);
this.executeExternalBeforeCalling();
callsParams.offlinePushInfo = Object.assign(Object.assign({}, this.getDefaultOfflinePushInfo()), offlinePushInfo);
const response = yield this._tuiCallEngine.calls(callsParams);
yield this._updateCallStoreAfterCall(userIDList, response);
}
catch (error) {
this._handleCallError(error, 'calls');
}
});
}
join(params) {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) === index_1.CallStatus.CONNECTED)
return; // avoid double click when application stuck
try {
const response = yield this._tuiCallEngine.join(params);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_CLICKABLE, true);
this.startTimer();
const updateStoreParams = {
[index_1.NAME.CALL_ROLE]: index_1.CallRole.CALLEE,
[index_1.NAME.IS_GROUP]: true,
[index_1.NAME.CALL_STATUS]: index_1.CallStatus.CONNECTED,
[index_1.NAME.CALL_MEDIA_TYPE]: TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE) || index_1.CallMediaType.AUDIO, // default audio callMediaType
};
TUIStore.updateStore(updateStoreParams, index_1.StoreName.CALL);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.PUSHER, response);
this.setSoundMode(params.type === index_1.CallMediaType.AUDIO ? index_1.AudioPlayBackDevice.EAR : index_1.AudioPlayBackDevice.SPEAKER);
const localUserInfo = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
yield this.openMicrophone();
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.AUDIO);
// // Current policy: By default, the camera remains off when joining GroupCall
// if (TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE) === CallMediaType.VIDEO) {
// await this.openCamera(NAME.LOCAL_VIDEO);
// setLocalUserInfoAudioVideoAvailable(true, NAME.VIDEO);
// }
}
catch (error) {
this._handleCallError(error, 'join');
}
});
}
// ===============================【其它对外接口】===============================
getTUICallEngineInstance() {
return (this === null || this === void 0 ? void 0 : this._tuiCallEngine) || null;
}
setLogLevel(level) {
var _a;
(_a = this === null || this === void 0 ? void 0 : this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.setLogLevel(level);
}
setLanguage(language) {
console.warn(`${index_1.NAME.PREFIX}The miniProgram does not support setLanguage`);
}
enableFloatWindow(enable) {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.ENABLE_FLOAT_WINDOW, enable);
}
setSelfInfo(params) {
return __awaiter(this, void 0, void 0, function* () {
const { nickName, avatar } = params;
try {
yield this._tuiCallEngine.setSelfInfo(nickName, avatar);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}setSelfInfo failed, error: ${error}.`);
}
});
}
enableVirtualBackground(enable) {
return __awaiter(this, void 0, void 0, function* () {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND, enable);
});
}
// 修改默认铃声:只支持本地铃声文件,不支持在线铃声文件;修改铃声修改的是被叫的铃声
setCallingBell(filePath) {
return __awaiter(this, void 0, void 0, function* () {
let isCheckFileExist = true;
if (!isCheckFileExist) {
console.warn(`${index_1.NAME.PREFIX}setCallingBell failed, filePath: ${filePath}.`);
return;
}
const bellParams = { calleeBellFilePath: filePath };
this._bellContext.setBellProperties(bellParams);
});
}
enableMuteMode(enable) {
return __awaiter(this, void 0, void 0, function* () {
try {
const bellParams = { isMuteBell: enable };
this._bellContext.setBellProperties(bellParams);
yield this._bellContext.setBellMute(enable);
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}enableMuteMode failed, error: ${error}.`);
}
});
}
hideFeatureButton(buttonName) {
uiDesign.hideFeatureButton(buttonName);
}
setLocalViewBackgroundImage(url) {
uiDesign.setLocalViewBackgroundImage(url);
}
setRemoteViewBackgroundImage(userId, url) {
uiDesign.setRemoteViewBackgroundImage(userId, url);
}
setLayoutMode(layoutMode) {
uiDesign.setLayoutMode(layoutMode);
}
setCameraDefaultState(isOpen) {
uiDesign.setCameraDefaultState(isOpen);
}
// =============================【实验性接口】=============================
callExperimentalAPI(jsonStr) {
var _a, _b;
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(index_1.StoreName.CALL, index_1.NAME.IS_FORCE_USE_V2_API, !!enable);
break;
default:
break;
}
}
catch (error) {
(_b = (_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.reportLog) === null || _b === void 0 ? void 0 : _b.call(_a, { name: 'TUICallKit.callExperimentalAPI.fail', data: { error } });
}
}
// =============================【内部按钮操作方法】=============================
accept() {
var _a, _b, _c, _d, _e, _f;
return __awaiter(this, void 0, void 0, function* () {
const callStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
(_b = (_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.reportLog) === null || _b === void 0 ? void 0 : _b.call(_a, {
name: 'TUICallKit.accept.start',
data: { callStatus },
});
if (callStatus === index_1.CallStatus.CONNECTED)
return; // avoid double click when application stuck, especially for miniProgram
try {
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
const deviceMap = {
microphone: true,
camera: callMediaType === index_1.CallMediaType.VIDEO,
};
const currentDevicePermission = yield this._tuiCallEngine.deviceCheck(deviceMap);
if (currentDevicePermission && !this._preDevicePermission) {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.PUSHER_ID, index_1.NAME.NEW_PUSHER);
this._preDevicePermission = currentDevicePermission;
}
const response = yield this._tuiCallEngine.accept();
if (response) {
// 小程序接通时会进行授权弹框, 状态需要放在 accept 后, 否则先接通后再拉起权限设置
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS, index_1.CallStatus.CONNECTED);
(_c = this._chatCombine) === null || _c === void 0 ? void 0 : _c.callTUIService({ message: (_d = response === null || response === void 0 ? void 0 : response.data) === null || _d === void 0 ? void 0 : _d.message });
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_CLICKABLE, true);
this.startTimer();
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
const isCameraDefaultStateClose = this._getFeatureButtonDefaultState(index_1.FeatureButton.Camera) === index_1.ButtonState.Close;
(callMediaType === index_1.CallMediaType.VIDEO) && !isCameraDefaultStateClose && (yield this.openCamera(index_1.NAME.LOCAL_VIDEO));
response.pusher && TUIStore.update(index_1.StoreName.CALL, index_1.NAME.PUSHER, response.pusher);
this.setSoundMode(callMediaType === index_1.CallMediaType.AUDIO ? index_1.AudioPlayBackDevice.EAR : index_1.AudioPlayBackDevice.SPEAKER);
const localUserInfo = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.AUDIO); // web && mini default open audio
}
}
catch (error) {
(_f = (_e = this._tuiCallEngine) === null || _e === void 0 ? void 0 : _e.reportLog) === null || _f === void 0 ? void 0 : _f.call(_e, {
name: 'TUICallKit.accept.fail',
level: 'error',
error,
});
if ((0, common_utils_1.handleRepeatedCallError)(error))
return;
(0, utils_1.noDevicePermissionToast)(error, index_1.CallMediaType.AUDIO, this._tuiCallEngine);
this._resetCallStore();
}
});
}
hangup() {
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) === index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const response = yield this._tuiCallEngine.hangup();
response === null || response === void 0 ? void 0 : response.forEach((item) => {
var _a, _b;
if ((item === null || item === void 0 ? void 0 : item.code) === 0) {
(_a = this._chatCombine) === null || _a === void 0 ? void 0 : _a.callTUIService({ message: (_b = item === null || item === void 0 ? void 0 : item.data) === null || _b === void 0 ? void 0 : _b.message });
}
});
}
catch (error) {
console.debug(error);
}
this._resetCallStore();
});
}
reject() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS) === index_1.CallStatus.IDLE)
return; // avoid double click when application stuck
try {
const response = yield this._tuiCallEngine.reject();
if ((response === null || response === void 0 ? void 0 : response.code) === 0) {
(_a = this._chatCombine) === null || _a === void 0 ? void 0 : _a.callTUIService({ message: (_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.message });
}
}
catch (error) {
console.debug(error);
}
this._resetCallStore();
});
}
openCamera(videoViewDomID) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (TUIGlobal.isH5 || TUIGlobal.isWeChat) {
const currentPosition = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CAMERA_POSITION);
const isFrontCamera = currentPosition === index_1.CameraPosition.FRONT ? true : false;
this._tuiCallEngine.openCamera(videoViewDomID, isFrontCamera);
}
else {
yield this._tuiCallEngine.openCamera(videoViewDomID);
}
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.VIDEO);
}
catch (error) {
(0, utils_1.noDevicePermissionToast)(error, index_1.CallMediaType.VIDEO, this._tuiCallEngine);
console.error(`${index_1.NAME.PREFIX}openCamera error: ${error}.`);
}
});
}
closeCamera() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this._tuiCallEngine.closeCamera();
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(false, index_1.NAME.VIDEO);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}closeCamera error: ${error}.`);
}
});
}
openMicrophone() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this._tuiCallEngine.openMicrophone();
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.AUDIO);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}openMicrophone failed, error: ${error}.`);
}
});
}
closeMicrophone() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this._tuiCallEngine.closeMicrophone();
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(false, index_1.NAME.AUDIO);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}closeMicrophone failed, error: ${error}.`);
}
});
}
switchScreen(userId) {
if (!userId)
return;
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.BIG_SCREEN_USER_ID, userId);
}
// support video to audio; not support audio to video
switchCallMediaType() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
try {
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
if (callMediaType === index_1.CallMediaType.AUDIO) {
console.warn(`${index_1.NAME.PREFIX}switchCallMediaType failed, ${callMediaType} not support.`);
return;
}
const response = yield this._tuiCallEngine.switchCallMediaType(index_1.CallMediaType.AUDIO);
if ((response === null || response === void 0 ? void 0 : response.code) === 0) {
(_a = this._chatCombine) === null || _a === void 0 ? void 0 : _a.callTUIService({ message: (_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.message });
}
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE, index_1.CallMediaType.AUDIO);
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
const oldStatus = isGroup ? index_1.StatusChange.CALLING_GROUP_VIDEO : index_1.StatusChange.CALLING_C2C_VIDEO;
const newStatus = (0, utils_1.generateStatusChangeText)();
this.statusChanged && this.statusChanged({ oldStatus, newStatus });
this.setSoundMode(index_1.AudioPlayBackDevice.EAR);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}switchCallMediaType failed, error: ${error}.`);
}
});
}
switchCamera() {
return __awaiter(this, void 0, void 0, function* () {
const currentPosition = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CAMERA_POSITION);
const targetPosition = currentPosition === index_1.CameraPosition.BACK ? index_1.CameraPosition.FRONT : index_1.CameraPosition.BACK;
try {
yield this._tuiCallEngine.switchCamera(targetPosition);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CAMERA_POSITION, targetPosition);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}_switchCamera failed, error: ${error}.`);
}
});
}
setSoundMode(type) {
var _a;
try {
let isEarPhone = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_EAR_PHONE);
const soundMode = type || (isEarPhone ? index_1.AudioPlayBackDevice.SPEAKER : index_1.AudioPlayBackDevice.EAR); // UI 层切换时传参数
(_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.selectAudioPlaybackDevice(soundMode);
if (type) {
isEarPhone = type === index_1.AudioPlayBackDevice.EAR;
}
else {
isEarPhone = !isEarPhone;
}
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_EAR_PHONE, isEarPhone);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}setSoundMode failed, error: ${error}.`);
}
}
setBlurBackground(enable) {
return __awaiter(this, void 0, void 0, function* () {
try {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.ENABLE_VIRTUAL_BACKGROUND, enable);
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}_setBlurBackground failed, error: ${error}.`);
}
});
}
// ==========================【TUICallEngine 事件处理】==========================
_addListenTuiCallEngineEvent() {
this._engineEventHandler.addListenTuiCallEngineEvent();
}
_removeListenTuiCallEngineEvent() {
this._engineEventHandler.removeListenTuiCallEngineEvent();
}
setCallback(params) {
const { beforeCalling, afterCalling, onMinimized, onMessageSentByMe, kickedOut, statusChanged } = params;
beforeCalling && (this.beforeCalling = beforeCalling);
afterCalling && (this.afterCalling = afterCalling);
onMinimized && (this.onMinimized = onMinimized);
onMessageSentByMe && (this.onMessageSentByMe = onMessageSentByMe);
kickedOut && (this.kickedOut = kickedOut);
statusChanged && (this.statusChanged = statusChanged);
}
toggleMinimize() {
const isMinimized = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_MINIMIZED);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_MINIMIZED, !isMinimized);
console.log(`${index_1.NAME.PREFIX}toggleMinimize: ${isMinimized} -> ${!isMinimized}.`);
this.onMinimized && this.onMinimized(isMinimized, !isMinimized);
}
executeExternalBeforeCalling() {
this.beforeCalling && this.beforeCalling();
}
executeExternalAfterCalling() {
this.afterCalling && this.afterCalling();
}
// 处理用户异常退出的情况, 小程序 ”右滑“、"左上角退出"; web 页面关闭浏览器或关闭 tab 页面
handleExceptionExit(event) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
try {
const callStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
const callRole = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_ROLE);
(_b = (_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.reportLog) === null || _b === void 0 ? void 0 : _b.call(_a, { name: 'TUICallkit.handleExceptionExit', data: { callStatus, callRole } });
if (callStatus === index_1.CallStatus.IDLE)
return;
// 在呼叫状态下,被叫调用 reject,主叫调用 hangup
if (callStatus === index_1.CallStatus.CALLING) {
if (callRole === index_1.CallRole.CALLER) {
yield (this === null || this === void 0 ? void 0 : this.hangup());
}
else {
yield (this === null || this === void 0 ? void 0 : this.reject());
}
}
if (callStatus === index_1.CallStatus.CONNECTED) {
yield (this === null || this === void 0 ? void 0 : this.hangup());
}
this === null || this === void 0 ? void 0 : this._resetCallStore();
}
catch (error) {
console.error(`${index_1.NAME.PREFIX} handleExceptionExit failed, error: ${error}.`);
}
if (event) {
event.returnValue = '';
}
});
}
// 处理 pusher 内部错误,没有 live-pusher 能力时做出弹窗提示。
handlePusherError(event) {
var _a;
if (((_a = event === null || event === void 0 ? void 0 : event.detail) === null || _a === void 0 ? void 0 : _a.errMsg) === 'fail:access denied') {
(0, miniProgram_1.handleNoPusherCapabilityError)();
}
}
// ========================【TUICallKit 组件属性设置方法】========================
setVideoDisplayMode(displayMode) {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.DISPLAY_MODE, displayMode);
}
setVideoResolution(resolution) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
if (!resolution)
return;
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.VIDEO_RESOLUTION, resolution);
yield ((_a = this._tuiCallEngine) === null || _a === void 0 ? void 0 : _a.setVideoQuality(resolution));
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}setVideoResolution failed, error: ${error}.`);
}
});
}
// 通话时长更新
startTimer() {
if (this._timerId === -1) {
this._startTimeStamp = (0, common_utils_1.performanceNow)();
this._timerId = timer_1.default.run(index_1.NAME.TIMEOUT, this._updateCallDuration.bind(this), { delay: 1000 });
}
}
// =========================【private methods for service use】=========================
// 处理 “呼叫” 抛出的异常
_handleCallError(error, methodName) {
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
if ((0, common_utils_1.handleRepeatedCallError)(error))
return;
(0, miniProgram_1.handlePackageError)(error); // 无套餐提示, 小程序 engine 不抛出 onError
(0, utils_1.noDevicePermissionToast)(error, index_1.CallMediaType.AUDIO, this._tuiCallEngine);
console.error(`${index_1.NAME.PREFIX}${methodName} failed, error: ${error}.`);
this._resetCallStore();
throw error;
}
_updateCallStoreBeforeCall(type, remoteUserInfoList, groupID) {
return __awaiter(this, void 0, void 0, function* () {
let callTips = index_2.CallTips.CALLER_CALLING_MSG;
if (groupID || TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_MINIMIZED) || remoteUserInfoList.length > 1) {
callTips = index_2.CallTips.CALLER_GROUP_CALLING_MSG;
}
let updateStoreParams = {
[index_1.NAME.CALL_MEDIA_TYPE]: type,
[index_1.NAME.CALL_ROLE]: index_1.CallRole.CALLER,
[index_1.NAME.REMOTE_USER_INFO_LIST]: remoteUserInfoList,
[index_1.NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST]: remoteUserInfoList,
[index_1.NAME.IS_GROUP]: (!!groupID || remoteUserInfoList.length > 1),
[index_1.NAME.CALL_TIPS]: callTips,
[index_1.NAME.GROUP_ID]: groupID
};
const pusher = { enableCamera: type === index_1.CallMediaType.VIDEO, enableMic: true }; // mini 默认打开麦克风
updateStoreParams = Object.assign(Object.assign({}, updateStoreParams), { [index_1.NAME.PUSHER]: pusher });
TUIStore.updateStore(updateStoreParams, index_1.StoreName.CALL);
const callStatus = yield (0, miniProgram_1.beforeCall)(type, this); // 如果没有权限, 此时为 false. 因此需要在 call 后设置为 calling. 和 web 存在差异
console.log(`${index_1.NAME.PREFIX}mini beforeCall return callStatus: ${callStatus}.`);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS, callStatus);
const remoteUserInfoLists = yield (0, utils_1.getRemoteUserProfile)(remoteUserInfoList.map(obj => obj.userId), this.getTim());
if (remoteUserInfoLists.length > 0) {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST, remoteUserInfoLists);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoLists);
}
const deviceMap = {
microphone: true,
camera: type === index_1.CallMediaType.VIDEO,
};
let hasDevicePermission = yield this._tuiCallEngine.deviceCheck(deviceMap);
if (!hasDevicePermission) {
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
this._permissionCheckTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
hasDevicePermission = yield this._tuiCallEngine.deviceCheck(deviceMap);
if (hasDevicePermission && this._permissionCheckTimer) {
clearInterval(this._permissionCheckTimer);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS, index_1.CallStatus.CALLING);
}
}), 500);
}
});
}
_updateCallStoreAfterCall(userIdList, response) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (response) {
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.IS_CLICKABLE, true);
(0, utils_1.updateRoomIdAndRoomIdType)(response === null || response === void 0 ? void 0 : response.roomID, response === null || response === void 0 ? void 0 : response.strRoomID);
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
(_a = this._chatCombine) === null || _a === void 0 ? void 0 : _a.callTUIService({ message: (_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.message });
response.pusher && TUIStore.update(index_1.StoreName.CALL, index_1.NAME.PUSHER, response.pusher);
this.setSoundMode(callMediaType === index_1.CallMediaType.AUDIO ? index_1.AudioPlayBackDevice.EAR : index_1.AudioPlayBackDevice.SPEAKER);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS, index_1.CallStatus.CALLING); // 小程序未授权时, 此时状态为 idle; web 直接设置为 calling
const isCameraDefaultStateClose = this._getFeatureButtonDefaultState(index_1.FeatureButton.Camera) === index_1.ButtonState.Close;
(callMediaType === index_1.CallMediaType.VIDEO) && !isCameraDefaultStateClose && (yield this.openCamera(index_1.NAME.LOCAL_VIDEO));
const localUserInfo = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, Object.assign(Object.assign({}, localUserInfo), { isEnter: true }));
(0, utils_1.setLocalUserInfoAudioVideoAvailable)(true, index_1.NAME.AUDIO); // web && mini, default open audio
}
else {
this._permissionCheckTimer && clearInterval(this._permissionCheckTimer);
this._permissionCheckTimer = null;
this._resetCallStore();
}
});
}
_getFeatureButtonDefaultState(buttonName) {
var _a;
const { button: buttonConfig } = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CUSTOM_UI_CONFIG);
return (_a = buttonConfig === null || buttonConfig === void 0 ? void 0 : buttonConfig[buttonName]) === null || _a === void 0 ? void 0 : _a.state;
}
_updateCallDuration() {
const callDurationNum = Math.round(((0, common_utils_1.performanceNow)() - this._startTimeStamp) / 1000); // miniProgram stop timer when background
const callDurationStr = (0, common_utils_1.formatTime)(callDurationNum);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CALL_DURATION, callDurationStr);
}
_stopTimer() {
if (this._timerId !== -1) {
timer_1.default.clearTask(this._timerId);
this._timerId = -1;
}
}
_resetCallStore() {
const oldStatusStr = (0, utils_1.generateStatusChangeText)();
this._stopTimer();
// localUserInfo, language 在通话结束后不需要清除
// callStatus 清除需要通知; isMinimized 也需要通知(basic-vue3 中切小窗关闭后, 再呼叫还是小窗, 因此需要通知到组件侧)
// isGroup 也不清除(engine 先抛 cancel 事件, 再抛 reject 事件)
// displayMode、videoResolution 也不能清除, 组件不卸载, 这些属性也需保留, 否则采用默认值.
// enableFloatWindow 不清除:开启/关闭悬浮窗功能。
let notResetOrNotifyKeys = Object.keys(index_1.CALL_DATA_KEY).filter((key) => {
switch (index_1.CALL_DATA_KEY[key]) {
case index_1.NAME.CALL_STATUS:
case index_1.NAME.LANGUAGE:
case index_1.NAME.IS_GROUP:
case index_1.NAME.DISPLAY_MODE:
case index_1.NAME.VIDEO_RESOLUTION:
case index_1.NAME.ENABLE_FLOAT_WINDOW:
case index_1.NAME.LOCAL_USER_INFO:
case index_1.NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND:
case index_1.NAME.IS_FORCE_USE_V2_API:
case index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN: {
return false;
}
default: {
return true;
}
}
});
notResetOrNotifyKeys = notResetOrNotifyKeys.map(key => index_1.CALL_DATA_KEY[key]);
TUIStore.reset(index_1.StoreName.CALL, notResetOrNotifyKeys);
const callStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
callStatus !== index_1.CallStatus.IDLE && TUIStore.reset(index_1.StoreName.CALL, [index_1.NAME.CALL_STATUS], true); // callStatus reset need notify
TUIStore.reset(index_1.StoreName.CALL, [index_1.NAME.IS_MINIMIZED], true); // isMinimized reset need notify
TUIStore.reset(index_1.StoreName.CALL, [index_1.NAME.IS_EAR_PHONE], true); // isEarPhone reset need notify
TUIStore.reset(index_1.StoreName.CALL, [index_1.NAME.ENABLE_VIRTUAL_BACKGROUND], true); // ENABLE_VIRTUAL_BACKGROUND reset need notify
TUIStore.reset(index_1.StoreName.CALL, [index_1.NAME.IS_MUTE_SPEAKER], true); // IS_MUTE_SPEAKER reset need notify
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO, Object.assign(Object.assign({}, TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO)), { isVideoAvailable: false, isAudioAvailable: false }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, Object.assign(Object.assign({}, TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN)), { isVideoAvailable: false, isAudioAvailable: false }));
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST, []);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, []);
TUIStore.update(index_1.StoreName.CALL, index_1.NAME.CAMERA_POSITION, index_1.CameraPosition.FRONT);
const newStatusStr = (0, utils_1.generateStatusChangeText)();
if (oldStatusStr !== newStatusStr) {
this.statusChanged && this.statusChanged({ oldStatus: oldStatusStr, newStatus: newStatusStr });
}
}
// =========================【Calling the Chat SDK APi】=========================
// 获取群成员
getGroupMemberList(count, offset) {
return __awaiter(this, void 0, void 0, function* () {
const groupID = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.GROUP_ID);
let groupMemberList = yield (0, utils_1.getGroupMemberList)(groupID, this.getTim(), count, offset);
return groupMemberList;
});
}
// 获取群信息
getGroupProfile() {
return __awaiter(this, void 0, void 0, function* () {
const groupID = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.GROUP_ID);
return yield (0, utils_1.getGroupProfile)(groupID, this.getTim());
});
}
_watchTUIStore() {
TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.watch(index_1.StoreName.CALL, {
[index_1.NAME.CALL_STATUS]: this._handleCallStatusChange,
});
}
_unwatchTUIStore() {
TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.unwatch(index_1.StoreName.CALL, {
[index_1.NAME.CALL_STATUS]: this._handleCallStatusChange,
});
}
// =========================【融合 chat 】=========================
bindTUICore(TUICore) {
this._TUICore = TUICore;
}
// =========================【set、get methods】=========================
getTim() {
var _a, _b;
if (this._tim)
return thi