import {action, observable} from 'mobx-miniprogram' import TIM from "tim-wx-sdk"; import Tim from "../model/tim"; import {toast} from "../utils/utils"; import EduTRTC from "../model/edu-trct"; import Api from "../model/api"; export const timStore = observable({ sdkReady: false, conversationList: [], messageList: [], maxMessageListLength: 1000, _targetUserId: null,//单聊对象id intoView: null,//结合scrollview 设置每个消息的id为xxx+index,然后设置intoView为消息长度-1,滑动到这个位置为最下方 isCompleted: false,//单聊消息是否下拉完了 canScroll: true,//群聊消息是否可以滑动 后期可以在触摸聊天内容的时候设置成false,在主动滑到最底部设置成true来提高用户体验 user: { nickname: '', avatar: '', gender: TIM.TYPES.GENDER_MALE, }, userId: '', userSig: '', groupId: '', groupReady: false, isCanShare: 0, isAudVideo: 0, isAudText: 0, isStuVideo: 0, handReply: false, //live pusher: {}, playerList: [], initLive: action(function (page) { try { EduTRTC.getInstance().initPage(page); this.bindTRTCRoomEvent() this.pusher = EduTRTC.getInstance().createPusher(); const config = { userID: this.userId, sdkAppID: Tim.SDKAppID, userSig: this.userSig, strRoomID: this.groupId, scene: 'live' } this.pusher = EduTRTC.getInstance().getSDK().enterRoom(config); } catch (e) { console.log("异常", e) } }), exitRoom: action(function () { const result = EduTRTC.getInstance().exitRoom() this.pusher = result.pusher; this.playerList = result.playerList; }), setScroll: action(function (canScroll) { this.canScroll = canScroll; }), setUser: action(async function (user, groupId, userSig) { if (this.userId != user.userID && this.groupId != groupId && this.sdkReady) { await this.logout() } this.user = user; this.userSig = userSig; this.userId = user.userID; this.groupId = groupId; }), login: action(async function () { if (this.sdkReady) { if (!this.groupReady) { await this.joinGroup(); } return; } this._runListener() await Tim.getInstance().login(this.userId, this.userSig) }), logout: action(async function () { await this.quitGroup(); await Tim.getInstance().logout() }), isReady: action(function () { return this.sdkReady }), quitGroup: action(async function () { this.groupReady = false; this.isCanShare = 0 this.isAudVideo = 0 this.isAudText = 0 this.isStuVideo = 0 await Tim.getInstance().quitGroup(this.groupId) }), getConversationList: action(async function () { this.conversationList = await Tim.getInstance().getConversationList() }), getMessageList: action(async function () { if (!this._targetUserId) { throw Error("未指定目标用户 id") } this.messageList = await Tim.getInstance() .reset() .getMessageList(this._targetUserId) this.intoView = this.messageList.length - 1 await Tim.getInstance().setMessageRead(this._targetUserId) }), setTargetUserId: action(function (targetUserId) { this._targetUserId = targetUserId }), pushMessage: action(function (message) { if (this.messageList.length === this.maxMessageListLength) { this.messageList.shift() } message = { name: message.nick || message.from, message: message.payload.text } this.messageList = this.messageList.concat([message]) this.intoView = this.messageList.length - 1 if (this.canScroll) { wx.pageScrollTo({scrollTop: 99999}) } }), scrollMessageList: action(async function () { const messageList = await Tim.getInstance() .getMessageList(this._targetUserId); this.intoView = this.messageList.length === Tim.getInstance().messageList.length ? messageList.length : messageList.length - 1 /** * tips * 1. MobX 中属性的值是 Array 的时候,他是一个被包装过的 Array,并非原生 Array,它是一个响应式对象 * 2. 经过包装的 Array 同样具备大多数原生 Array 所具备的方法。 * 3. 想把响应式的对象数组变成普通数组,可以调用slice()函数遍历所有对象元素生成一个新的普通数组 */ this.messageList = messageList.concat(this.messageList.slice()) }), resetMessage: action(function () { this.messageList = [] this._targetUserId = null this.intoView = 0 this.isCompleted = false }), _runListener() { const sdk = Tim.getInstance().getSDK(); sdk.on(TIM.EVENT.SDK_READY, this.onSDKReady, this) sdk.on(TIM.EVENT.SDK_NOT_READY, this.onSdkNotReady, this); sdk.on(TIM.EVENT.KICKED_OUT, this.onSdkNotReady, this); sdk.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated, this); sdk.on(TIM.EVENT.MESSAGE_RECEIVED, this._handleMessageReceived, this); sdk.on(TIM.EVENT.ERROR, this._handleError, this); }, async joinGroup() { //加入群 this.resetMessage(); const groupCustomField = await Tim.getInstance().joinGroup(this.groupId); if (groupCustomField) { groupCustomField.forEach(item => { this[item.key] = item.value; }) } if (this.isCanShare == 1) { wx.showShareMenu(); } else { wx.hideShareMenu(); } this.groupReady = true; this.pushMessage({ nick: '系统通知', payload: { text: `欢迎${this.user.nickname}加入` } }) }, async onConversationListUpdated(event) { if (!event.data.length) { return } this.conversationList = event.data const unreadCount = event.data.reduce( (sum, item) => sum + item.unreadCount, 0) }, async onSDKReady() { this.sdkReady = true //SDK准备好了 更新用户信息 await Tim.getInstance().updateMyProfile(this.user) await this.joinGroup(); }, onSdkNotReady() { this.sdkReady = false this.groupReady = false this.isCanShare = 0 this.isAudVideo = 0 this.isAudText = 0 this.isStuVideo = 0 const sdk = Tim.getInstance().getSDK() sdk.off(TIM.EVENT.SDK_READY, this.onSDKReady); sdk.off(TIM.EVENT.SDK_NOT_READY, this.onSdkNotReady); sdk.off(TIM.EVENT.KICKED_OUT, this.onSdkNotReady); sdk.off(TIM.EVENT.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated); sdk.off(TIM.EVENT.MESSAGE_RECEIVED, this._handleMessageReceived); sdk.off(TIM.EVENT.ERROR, this._handleError); }, async _handleMessageReceived(event) { console.log("收到消息", event) const filterMsgs = event.data .filter(item => (item.to === this.groupId || item.to === this.userId)); for (const message of filterMsgs) { if (message.type == TIM.TYPES.MSG_GRP_SYS_NOTICE) { let text = this.parseGroupSystemNotice(message.payload); message.nick = "系统通知" message.payload.text = text console.log(text); } if (message.type == TIM.TYPES.MSG_GRP_TIP) { let text = this.parseGroupTipContent(message.payload); message.nick = "系统提示" message.payload.text = text console.log(text); } if (message.type === TIM.TYPES.MSG_GRP_TIP) { let groupCustomField = message.payload?.newGroupProfile?.groupCustomField; if (groupCustomField) { groupCustomField.forEach(item => { console.log('收到群资料变更', item.key, item.value); this[item.key] = item.value; }) } if (this.isCanShare == 1) { wx.showShareMenu(); } else { wx.hideShareMenu(); } } if (message.type === TIM.TYPES.MSG_CUSTOM) { await this.parseGroupCustom(message.payload); } if (message.type === TIM.TYPES.MSG_TEXT) { this.pushMessage(message) } } // if (!this._targetUserId) { // return // } // // const currentConversationMessage = event.data // .filter(item => item.from === this._targetUserId) // if (currentConversationMessage.length) { // this.messageList = this.messageList.concat(currentConversationMessage) // this.intoView = this.messageList.length - 1 // await Tim.getInstance().setMessageRead(this._targetUserId) // } }, _handleError(event) { console.log("IM错误", event) // event.name - TIM.EVENT.ERROR // event.data.code - 错误码 // event.data.message - 错误信息 }, parseGroupSystemNotice(payload) { const groupName = payload.groupProfile.groupName || payload.groupProfile.groupID switch (payload.operationType) { case 1: return `${payload.operatorID} 申请加入群组:${groupName}` case 2: return `成功加入群组:${groupName}` case 3: return `申请加入群组:${groupName}被拒绝` case 4: return `被管理员${payload.operatorID}踢出群组:${groupName}` case 5: toast('直播结束'); wx.navigateBack(); return `群:${groupName} 已被${payload.operatorID}解散` case 6: return `${payload.operatorID}创建群:${groupName}` case 7: return `${payload.operatorID}邀请你加群:${groupName}` case 8: return `你退出群组:${groupName}` case 9: return `你被${payload.operatorID}设置为群:${groupName}的管理员` case 10: return `你被${payload.operatorID}撤销群:${groupName}的管理员身份` case 255: return '自定义群系统通知' } }, parseGroupTipContent(payload) { switch (payload.operationType) { case TIM.TYPES.GRP_TIP_MBR_PROFILE_UPDATED: // 群成员资料变更,例如:群成员被禁言 const memberList = message.payload.memberList; for (let member of memberList) { console.log(`${member.userID} 被禁言${member.muteTime}秒`); } break; case TIM.TYPES.GRP_TIP_MBR_JOIN: return `群成员:${payload.userIDList.join(',')},加入群组` case TIM.TYPES.GRP_TIP_MBR_QUIT: return `群成员:${payload.userIDList.join(',')},退出群组` case TIM.TYPES.GRP_TIP_MBR_KICKED_OUT: return `群成员:${payload.userIDList.join(',')},被${payload.operatorID}踢出群组` case TIM.TYPES.GRP_TIP_MBR_SET_ADMIN: return `群成员:${payload.userIDList.join(',')},成为管理员` case TIM.TYPES.GRP_TIP_MBR_CANCELED_ADMIN: return `群成员:${payload.userIDList.join(',')},被撤销管理员` default: return '[群提示消息]' } }, async parseGroupCustom(payload) { let data = JSON.parse(payload.data); let to = data.to; let version = data.version; let action = data.action; if (to == this.userId) { if (version == Tim.VERSION) { switch (action) { case Tim.IM_ACTION_HAND: const res = await wx.showModal({ title: "老师邀请你上麦", confirmText: '同意', cancelText: '拒绝' }) if (res.confirm) { await Tim.getInstance().sendCMD(Tim.getInstance().createHandOKMsg(data.time, this.groupId)); this.setPusherAttributesHandler({enableCamera: true, enableMic: true}) this.uploadLink(); } else { await Tim.getInstance().sendCMD(Tim.getInstance().createHandCancelMsg(data.time, this.groupId)); this.setPusherAttributesHandler({enableCamera: false, enableMic: false}) } break case Tim.IM_ACTION_HAND_OK: toast("老师同意连麦") this.setPusherAttributesHandler({enableCamera: true, enableMic: true}) this.uploadLink(); break case Tim.IM_ACTION_HAND_CANCEL: toast("老师拒绝连麦") this.setPusherAttributesHandler({enableCamera: false, enableMic: false}) break case Tim.IM_ACTION_QUIT_LINK: this.setPusherAttributesHandler({enableCamera: false, enableMic: false}) toast("您已被老师下麦"); break } } } }, uploadLink(){ try { Api.uploadLinkOk({ scheduleIdStr: this.groupId, eStuIdStr: this.userId.split("_")[1], }); }catch (e){ console.log(e) } }, bindTRTCRoomEvent() { let sdk = EduTRTC.getInstance().getSDK(); const TRTC_EVENT = sdk.EVENT // 初始化事件订阅 sdk.on(TRTC_EVENT.LOCAL_JOIN, (event) => { console.log('本地加入房间', event) }) sdk.on(TRTC_EVENT.LOCAL_LEAVE, (event) => { console.log('本地离开房间', event) }) sdk.on(TRTC_EVENT.KICKED_OUT, (event) => { console.log('服务端踢人或房间被解散退房', event) }) sdk.on(TRTC_EVENT.ERROR, (event) => { console.log('TRTC错误', event) }) sdk.on(TRTC_EVENT.REMOTE_USER_JOIN, (event) => { console.log('远端用户加入', event) }) sdk.on(TRTC_EVENT.REMOTE_USER_LEAVE, (event) => { console.log('远端用户离开', event) const {userID, playerList} = event.data this.playerList = event.data.playerList }) sdk.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => { console.log('远端用户推送视频', event) const {player} = event.data // 开始播放远端的视频流,默认是不播放的 this.setPlayerAttributesHandler(player, {muteVideo: false}) }) sdk.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event) => { console.log('远端用户取消推送视频', event) const {player} = event.data this.setPlayerAttributesHandler(player, {muteVideo: true}) }) sdk.on(TRTC_EVENT.REMOTE_AUDIO_ADD, (event) => { console.log('远端用户推送音频', event) const {player} = event.data this.setPlayerAttributesHandler(player, {muteAudio: false}) }) sdk.on(TRTC_EVENT.REMOTE_AUDIO_REMOVE, (event) => { console.log('远端用户取消推送音频', event) const {player} = event.data this.setPlayerAttributesHandler(player, {muteAudio: true}) }) sdk.on(TRTC_EVENT.REMOTE_AUDIO_VOLUME_UPDATE, (event) => { console.log('远端用户音频大小更新', event) this.playerList = event.data.playerList }) sdk.on(TRTC_EVENT.LOCAL_AUDIO_VOLUME_UPDATE, (event) => { console.log('本地用户音频大小更新', event) this.pusher = event.data.pusher }) }, // 设置 pusher 属性 setPusherAttributesHandler(options) { let sdk = EduTRTC.getInstance().getSDK(); this.pusher = sdk.setPusherAttributes(options); this.handReply = !this.handReply; }, // 设置某个 player 属性 setPlayerAttributesHandler(player, options) { let sdk = EduTRTC.getInstance().getSDK(); this.playerList = sdk.setPlayerAttributes(player.streamID, options); }, })