337 lines
13 KiB
JavaScript
337 lines
13 KiB
JavaScript
import { defineStore } from 'pinia';
|
||
import {
|
||
CALLSTATUS,
|
||
CALL_TYPES,
|
||
CALL_TYPE,
|
||
CALL_INVITE_TEXT,
|
||
} from '../contants';
|
||
import {
|
||
CALLKIT_EVENT_CODE,
|
||
CALLKIT_EVENT_TYPE,
|
||
} from '../contants/callKitEvent';
|
||
import { useInitCallKit } from '../index';
|
||
import useCallKitEvent from '../callKitManage/useCallKitEvent';
|
||
import useSendSignalMsgs from '../callKitManage/useSendSignalMsgs';
|
||
import createUid from '../utils/createUid';
|
||
const { EVENT_NAME, PUB_CHANNEL_EVENT } = useCallKitEvent();
|
||
|
||
const useAgoraChannelStore = defineStore('agoraChannelStore', {
|
||
state: () => ({
|
||
emClientInfos: {
|
||
apiUrl: '',
|
||
appKey: '',
|
||
loginUserId: '',
|
||
clientResource: '',
|
||
accessToken: '',
|
||
},
|
||
callKitStatus: {
|
||
localClientStatus: CALLSTATUS.idle, //callkit状态
|
||
channelInfos: {
|
||
channelName: '', //频道名
|
||
agoraChannelToken: '', //频道token
|
||
agoraUserId: '', //频道用户id,
|
||
callType: CALL_TYPES.SINGLE_VOICE, //0 语音 1 视频 2 多人音视频
|
||
callId: null, //会议ID
|
||
channelUsers: {}, //频道内用户
|
||
callerDevId: '', //主叫方设备ID
|
||
calleeDevId: '', //被叫方设备ID
|
||
callerIMName: '', //主叫方环信ID
|
||
calleeIMName: '', //被叫方环信ID
|
||
groupId: '', //群组ID
|
||
},
|
||
//被邀请对象 单人为string 多人为array
|
||
inviteTarget: null,
|
||
},
|
||
callKitTimer: null,
|
||
}),
|
||
actions: {
|
||
/* emClient */
|
||
initEmClientInfos(emClient) {
|
||
console.log('initEmClientInfos', emClient);
|
||
if (!emClient) return;
|
||
this.emClientInfos.apiUrl = emClient.apiUrl;
|
||
this.emClientInfos.appKey = emClient.appKey;
|
||
this.emClientInfos.loginUserId = emClient.user;
|
||
this.emClientInfos.accessToken = emClient.token;
|
||
this.emClientInfos.clientResource = emClient.clientResource;
|
||
},
|
||
/* CallKit status 管理 */
|
||
//初始化频道信息
|
||
initChannelInfos() {
|
||
this.callKitStatus.localClientStatus = CALLSTATUS.idle;
|
||
this.callKitStatus.channelInfos = {
|
||
channelName: '', //频道名
|
||
agoraChannelToken: '', //频道token
|
||
agoraUid: '', //频道用户id
|
||
callType: CALL_TYPES.SINGLE_VOICE, //0 语音 1 视频 2 多人音视频
|
||
callId: null, //会议ID
|
||
channelUsers: {}, //频道内用户
|
||
callerDevId: '', //主叫方设备ID
|
||
calleeDevId: '', //被叫方设备ID
|
||
confrontId: '', //要处理的目标ID
|
||
callerIMName: '', //主叫方环信ID
|
||
calleeIMName: '', //被叫方环信ID
|
||
groupId: '', //群组ID
|
||
};
|
||
this.callKitStatus.inviteTarget = null;
|
||
this.callKitTimer && clearTimeout(this.callKitTimer);
|
||
},
|
||
//更新localStatus
|
||
updateLocalStatus(typeCode) {
|
||
console.log('>>>>>开始变更本地状态为 typeCode', typeCode);
|
||
this.callKitStatus.localClientStatus = typeCode;
|
||
},
|
||
//更新频道信息
|
||
updateChannelInfos(msgBody) {
|
||
console.log('触发更新频道信息', msgBody);
|
||
const { from, to, ext } = msgBody || {};
|
||
const params = {
|
||
channelName:
|
||
ext.channelName || this.callKitStatus.channelInfos.channelName,
|
||
callId: ext.callId || this.callKitStatus.channelInfos.callId,
|
||
callType:
|
||
CALL_TYPE[ext.type] || this.callKitStatus.channelInfos.callType,
|
||
callerDevId: ext.callerDevId || 0,
|
||
calleeDevId: ext.calleeDevId,
|
||
callerIMName: from,
|
||
calleeIMName: to,
|
||
groupId: ext?.ext?.groupId ? ext.ext.groupId : '',
|
||
};
|
||
console.log('%c将要更新的信息内容为', 'color:red', params);
|
||
Object.assign(this.callKitStatus.channelInfos, params);
|
||
},
|
||
/* CallKit Timer */
|
||
//用作邀请信息发送之后发起计时30s。
|
||
startCallKitTimer() {
|
||
const { sendCannelMsg } = useSendSignalMsgs();
|
||
if (this.callKitTimer) {
|
||
clearTimeout(this.callKitTimer);
|
||
this.callKitTimer = null;
|
||
}
|
||
//对外发布应答事件
|
||
this.callKitTimer = setTimeout(() => {
|
||
const targetId = this.callKitStatus.inviteTarget;
|
||
//发送cannel信令
|
||
sendCannelMsg({
|
||
targetId,
|
||
callId: this.callKitStatus.channelInfos.callId,
|
||
});
|
||
const eventParams = {
|
||
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.TIMEOUT],
|
||
ext: { message: '通话超时未接听' },
|
||
callType: this.callKitStatus.channelInfos.callType,
|
||
eventHxId: targetId,
|
||
};
|
||
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
|
||
this.updateLocalStatus(CALLSTATUS.idle); //更改状态为闲置
|
||
}, 30000);
|
||
},
|
||
/* 多人会议使用 获取群组内的成员 */
|
||
getTheGroupMembers(groupId) {
|
||
const { CallKitEMClient } = useInitCallKit();
|
||
if (!groupId) return;
|
||
return new Promise((resolve, reject) => {
|
||
let pageNum = 1,
|
||
pageSize = 100;
|
||
let option = {
|
||
pageNum: pageNum,
|
||
pageSize: pageSize,
|
||
groupId: groupId,
|
||
};
|
||
CallKitEMClient.listGroupMembers(option)
|
||
.then((res) => resolve(res?.data))
|
||
.catch((err) => reject(err));
|
||
});
|
||
},
|
||
//发起音视频邀请
|
||
async sendInviteMessage(targetId, callType, groupId) {
|
||
console.log('>>>>>>>>>', targetId, callType, groupId);
|
||
const { sendInviteMsg } = useSendSignalMsgs();
|
||
//非空闲状态直接拒绝发送邀请信息[除了多人,因为涉及到多人通话中需要邀请他人入会]
|
||
if (
|
||
callType !== 2 &&
|
||
this.callKitStatus.localClientStatus !== CALLSTATUS.idle
|
||
)
|
||
return;
|
||
const channelInfors = {
|
||
channelName: `${callType}_${createUid()}`, //频道名
|
||
callId: createUid(),
|
||
inviteMsgContent: CALL_INVITE_TEXT[callType],
|
||
groupId: groupId, //只有为群聊多人邀请时这个参数才有用
|
||
};
|
||
|
||
this.callKitStatus.inviteTarget = targetId;
|
||
try {
|
||
//如果为数组就遍历发送
|
||
if (Array.isArray(targetId)) {
|
||
targetId.forEach((userId) => {
|
||
sendInviteMsg(userId, callType, channelInfors);
|
||
});
|
||
console.log('>>>>>群组多人邀请开始遍历发消息');
|
||
} else {
|
||
//非数组就单条发送
|
||
await sendInviteMsg(targetId, callType, channelInfors);
|
||
}
|
||
console.log('channelInforschannelInfors', channelInfors);
|
||
this.updateLocalStatus(CALLSTATUS.inviting);
|
||
} catch (error) {
|
||
console.log('%c邀请信息发送失败', 'color:red', error);
|
||
}
|
||
//更改部分ChannelInfos
|
||
const params = {
|
||
from: this.emClientInfos.loginUserId,
|
||
to: callType === CALL_TYPES.MULTI_VIDEO ? '' : targetId,
|
||
ext: {
|
||
channelName: channelInfors.channelName,
|
||
callId: channelInfors.callId,
|
||
type: callType,
|
||
callerDevId: this.emClientInfos.clientResource,
|
||
},
|
||
};
|
||
//如果存在群组ID则增加ext字段进入到groupId
|
||
if (callType === CALL_TYPES.MULTI_VIDEO && groupId)
|
||
params.ext.ext = { groupId };
|
||
console.log('邀请发送 callType为', callType);
|
||
this.updateChannelInfos(params);
|
||
//单人邀请开启超时挂断,多人则忽略
|
||
if (callType !== CALL_TYPES.MULTI_VIDEO) {
|
||
this.startCallKitTimer();
|
||
}
|
||
},
|
||
//【多人】在会议中邀请邀请--会议中邀请不生成新的频道信息
|
||
async inMultiChanelSendInviteMsg(targetId, callType) {
|
||
const { sendInviteMsg } = useSendSignalMsgs();
|
||
if (!targetId) throw 'targetId must pass!';
|
||
if (callType === undefined || callType === null || callType < 0)
|
||
throw 'callType must pass!';
|
||
if (
|
||
(Array.isArray(targetId) && targetId.length < 1) ||
|
||
targetId.length > 15
|
||
)
|
||
throw 'targetId length > 15 or length < 1';
|
||
|
||
const channelInfors = {
|
||
channelName: this.callKitStatus.channelInfos.channelName, //频道名
|
||
callId: this.callKitStatus.channelInfos.callId,
|
||
inviteMsgContent: CALL_INVITE_TEXT[callType],
|
||
groupId: this.callKitStatus.channelInfos.groupId, //只有为群聊多人邀请时这个参数才有用
|
||
};
|
||
this.callKitStatus.inviteTarget = targetId;
|
||
try {
|
||
//如果为数组就遍历发送
|
||
if (Array.isArray(targetId)) {
|
||
targetId.forEach((userId) => {
|
||
sendInviteMsg(userId, callType, channelInfors);
|
||
});
|
||
console.log('>>>>>群组多人邀请开始遍历发消息');
|
||
}
|
||
} catch (error) {
|
||
console.log('%c邀请信息发送失败', 'color:red', error);
|
||
}
|
||
},
|
||
//发送挂断信令
|
||
handleCancelCall() {
|
||
const { sendCannelMsg } = useSendSignalMsgs();
|
||
const targetId = this.callKitStatus.inviteTarget;
|
||
if (!targetId) return console.log('>>>挂断目标ID为空', targetId);
|
||
//多人遍历发送取消
|
||
if (this.callKitStatus.channelInfos.callType === CALL_TYPES.MULTI_VIDEO) {
|
||
targetId.length &&
|
||
targetId.forEach((userHxId) => {
|
||
sendCannelMsg({
|
||
targetId: userHxId,
|
||
callId: this.callKitStatus.channelInfos.callId,
|
||
});
|
||
});
|
||
//对外频道接听事件发布事件
|
||
const eventParams = {
|
||
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.CANCEL],
|
||
ext: { message: '多人音视频通话已取消' },
|
||
callType: CALL_TYPES.MULTI_VIDEO,
|
||
eventHxId: this.callKitStatus.channelInfos.groupId,
|
||
};
|
||
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
|
||
this.updateLocalStatus(CALLSTATUS.idle);
|
||
} else {
|
||
sendCannelMsg({
|
||
targetId,
|
||
callId: this.callKitStatus.channelInfos.callId,
|
||
});
|
||
//对外频道接听事件发布事件
|
||
const eventParams = {
|
||
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.CANCEL],
|
||
ext: { message: '通话已取消' },
|
||
callType: this.callKitStatus.channelInfos.callType,
|
||
eventHxId: targetId,
|
||
};
|
||
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
|
||
this.updateLocalStatus(CALLSTATUS.idle);
|
||
}
|
||
},
|
||
//请求频道Token
|
||
/**
|
||
* @function requestRtcChannelToken
|
||
* 该方法主要作用为请求Agora Rtc频道对应的token,其积极可抽象理解为拿到某频道对应的门钥匙。
|
||
* !但此方法所请求的接口为环信内部Demo演示接口,仅供环信内部自己使用如自己项目使用。请找后端协助部署一个类似的接口服务。
|
||
*/
|
||
requestRtcChannelToken() {
|
||
const { channelName } = this.callKitStatus.channelInfos;
|
||
const { apiUrl, appKey, loginUserId, accessToken } = this.emClientInfos;
|
||
const requestUrl = `${apiUrl}/token/rtcToken/v1?userAccount=${loginUserId}&channelName=${channelName}&appkey=${encodeURIComponent(
|
||
appKey
|
||
)}`;
|
||
return new Promise((resolve, reject) => {
|
||
uni.request({
|
||
url: requestUrl,
|
||
header: {
|
||
Authorization: `Bearer ${accessToken}`, //自定义请求头信息
|
||
},
|
||
success: (result) => {
|
||
console.log('>>>>>频道token已获取', result?.data);
|
||
resolve(result?.data);
|
||
},
|
||
fail: (e) => {
|
||
console.error('>>>>rtc token 获取失败', e);
|
||
uni.showToast({ icon: 'none', title: 'rtc token 请求失败' });
|
||
reject(e);
|
||
},
|
||
});
|
||
});
|
||
},
|
||
|
||
/**
|
||
* @function requestInChannelMapHxId
|
||
* 该方法作用为拿到频道内uid与环信id的映射关系,例如在频道内展示uid与之对应的环信ID,因此需要该接口取到uid的映射关系,非必须接口,
|
||
* 但此Demo多人音视频通话中有用到。
|
||
* !同样此方法如果项目中需要类似的需求,也请后端协助搭建类型功能接口,供项目中使用。
|
||
*/
|
||
//请求频道内uid映射的环信id
|
||
requestInChannelMapHxId() {
|
||
const { channelName } = this.callKitStatus.channelInfos;
|
||
const { apiUrl, appKey, loginUserId, accessToken } = this.emClientInfos;
|
||
const requestUrl = `${apiUrl}/channel/mapper?userAccount=${loginUserId}&channelName=${channelName}&appkey=${encodeURIComponent(
|
||
appKey
|
||
)}`;
|
||
console.log('>>>>requestUrl', `Bearer ${accessToken}`);
|
||
return new Promise((resolve, reject) => {
|
||
uni.request({
|
||
url: requestUrl,
|
||
header: {
|
||
Authorization: `Bearer ${accessToken}`, //自定义请求头信息
|
||
},
|
||
success: (result) => {
|
||
console.log('result', result?.data);
|
||
resolve(result?.data);
|
||
},
|
||
fail: (e) => {
|
||
console.error('>>>>rtc token 获取失败', e);
|
||
uni.showToast({ icon: 'none', title: 'rtc token 请求失败' });
|
||
reject(e);
|
||
},
|
||
});
|
||
});
|
||
},
|
||
},
|
||
});
|
||
export default useAgoraChannelStore;
|