瀏覽代碼

update 通话邀请

warrior 4 月之前
父節點
當前提交
b8fe1b5134

+ 8 - 1
projects/live-app/src/app/components/live/live.component.ts

@@ -64,7 +64,14 @@ export class LiveComponent implements OnInit {
       const alert = await this.alertController.create({
         header: '提示',
         message: '房间号不存在',
-        buttons: ['Action'],
+        buttons:[
+          {
+            text: '确定',
+            handler: () => {
+              history.back();
+            },
+          },
+        ]
       });
       await alert.present();
     }

+ 40 - 6
projects/live-app/src/moduls/user/profile/profile.component.ts

@@ -12,10 +12,10 @@ import {
 import * as Parse from 'parse';
 import { ImagePreviewComponent } from '../../../app/components/image-preview/image-preview.component';
 import { AiChatService } from '../../../services/aichart.service';
-import { HttpService } from '../../../services/http.service';
 import { UploadComponent } from '../../../app/components/upload/upload.component';
 import { GiftModalComponent } from '../../../app/components/gift-modal/gift-modal.component';
 import { MessageService } from '../../../services/message.service';
+import { ConnectTaskService } from '../../../services/connectTask.service';
 
 @Component({
   selector: 'app-profile',
@@ -55,25 +55,40 @@ export class ProfileComponent implements OnInit {
   room?: Parse.Object;
   @ViewChild('gift') gift!: GiftModalComponent;
   iscall: boolean = false;
+  isLiveing: boolean = false; // 是否在直播通话中
+  userStatus:string = 'OFFLINE';
   constructor(
     private activateRoute: ActivatedRoute,
     private router: Router,
     public toastController: ToastController,
     public loadingCtrl: LoadingController,
     private aiChatServ: AiChatService,
-    private http: HttpService,
     private alertController: AlertController,
+    private connectTask: ConnectTaskService,
     private msgSer: MessageService
-  ) {}
+  ) {
+    this.msgSer.event$.subscribe((data) => {
+      this.inviteCallback(data);
+    });
+  }
 
   ngOnInit() {
     this.activateRoute.paramMap.subscribe(async (params) => {
       let id: any = params.get('id');
       this.uid = id;
       await this.refresh();
+      this.userStatus = await this.connectTask.getState(this.uid,this.uid)
       this.getRoom();
     });
   }
+  ngOnDestroy(): void {
+    //Called once, before the instance is destroyed.
+    //Add 'implements OnDestroy' to the class.
+    if (this.isLiveing && this.uid !== this.currentUser?.id){
+      console.log('断开连接');
+      this.msgSer.unsubscribeMessage(this.uid);
+    }
+  }
   async refresh() {
     const loading = await this.loadingCtrl.create({
       message: '加载中',
@@ -207,9 +222,12 @@ export class ProfileComponent implements OnInit {
     this.router.navigate(['live/chat/' + this.uid]);
   }
   async toLiveContact() {
+    this.userStatus = await this.connectTask.getState(this.uid,this.uid)
+    console.log(this.userStatus);
     const alert = await this.alertController.create({
       cssClass: 'my-custom-class',
       header: '邀请通话',
+      backdropDismiss: false,
       message: '你将与对方发起私聊通话',
       buttons: [
         {
@@ -228,14 +246,29 @@ export class ProfileComponent implements OnInit {
     });
     await alert.present();
   }
+  async inviteCallback(event: boolean) {
+    console.log(event);
+    if (event == undefined) return;
+    this.iscall = false;
+    const toast = await this.toastController.create({
+      message: `对方${event ? '已接受' : '拒绝'}通话邀请`,
+      color: 'warning',
+      duration: 1500,
+    });
+    toast.present();
+    if (event) {
+      this.isLiveing = true;
+      this.userStatus = await this.connectTask.getState(this.uid,this.uid)
+      this.router.navigate(['live/link-room/' + this.room?.id]);
+    }
+  }
   async sendVideoCallInvite() {
     this.iscall = true;
     // this.router.navigate(['live/link-room/' + this.room?.id]);
-    await this.msgSer.subscribeMessage(this.user?.id!, { message: true }); //进入对方主播频道发送聊天邀请
-    this.msgSer.publishMessage('USERCALLINVITATION', this.user?.id!);
+    await this.msgSer.subscribeMessage(this.uid, { message: true }); //进入对方主播频道发送聊天邀请
+    this.msgSer.publishMessage('USERCALLINVITATION', this.uid);
   }
   async onCloseCall() {
-    await this.msgSer.publishMessage('CLOASE', this.user?.id!);
     this.iscall = false;
     const toast = await this.toastController.create({
       message: '已取消视频通话邀请',
@@ -243,6 +276,7 @@ export class ProfileComponent implements OnInit {
       duration: 1500,
     });
     toast.present();
+    this.msgSer.publishMessage('CLOASEINVITATION', this.uid);
   }
   onSendGift() {
     console.log('点击送出礼物');

+ 33 - 7
projects/live-app/src/services/connectTask.service.ts

@@ -17,7 +17,7 @@ export class ConnectTaskService {
     await this.anchorOnline();
     this.getOnlieUserList(this.msChannelName, 'MESSAGE');
   }
-  reset(){
+  reset() {
     this.onlineUserList = [];
     this.isSubscribe = false;
   }
@@ -29,13 +29,18 @@ export class ConnectTaskService {
       let nowChannes = await this.getWhereNow(uid);
       console.log('用户已订阅频道:', nowChannes);
       // if (!nowChannes.includes(this.msChannelName)) {
-        console.log('订阅成功');
-        /* 主播订阅主播频道 */
-        this.msgSer.subscribeMessage(this.msChannelName); //主播开启并订阅自己的聊天频道
+      console.log('订阅成功');
+      /* 主播订阅主播频道 */
+      await this.msgSer.subscribeMessage(this.msChannelName); //主播开启并订阅自己的聊天频道
       // }
       // if (!nowChannes.includes(uid)) {
-        this.msgSer.subscribeMessage(uid, { message: true }); //开启并订阅自己的聊天频道
+      await this.msgSer.setConnectState(uid, 'ONLINE'); //默认在线状态
+      await this.msgSer.subscribeMessage(uid, {//开启并订阅自己的聊天频道
+        message: true,
+        presence: true,
+      }); 
       // }
+      this.isSubscribe = true;
     }
   }
 
@@ -61,6 +66,27 @@ export class ConnectTaskService {
     }
   }
 
+  /* 获取状态 */
+  async getState(uid: string, channelName: string, channelType?: string) {
+    try {
+      const result = await this.msgSer.rtmClient.presence.getState(
+        uid,
+        channelName,
+        channelType ?? 'MESSAGE'
+      );
+      const { states, userId, statesCount } = result; // Tony 的临时状态
+      console.log(states.Mode);
+      console.log(result);
+      return states.Mode;
+    } catch (status: any) {
+      const { operation, reason, errorCode } = status;
+      console.log(
+        `${operation} failed, ErrorCode: ${errorCode}, because of: ${reason}.`
+      );
+      return 'OFFLINE';
+    }
+  }
+
   /* 获取在线用户列表 */
   async getOnlieUserList(
     channelName: string,
@@ -68,12 +94,11 @@ export class ConnectTaskService {
     channelType?: string
   ) {
     if (this.onlineUserList.length > 0) return;
-    console.log('获取在线用户列表');
     const options: any = {
       includedUserId: true,
       includedState: true,
     };
-    if(page) options.page = page;
+    if (page) options.page = page;
     try {
       const result = await this.msgSer.rtmClient.presence.whoNow(
         channelName,
@@ -87,6 +112,7 @@ export class ConnectTaskService {
         const { states, userId, statesCount } = userInfo;
         this.onlineUserList.push(userId);
       });
+      console.log('获取在线用户列表:',this.onlineUserList);
       if (nextPage) this.getOnlieUserList(channelName, nextPage, channelType);
     } catch (status: any) {
       const { operation, reason, errorCode } = status;

+ 29 - 36
projects/live-app/src/services/live.service.ts

@@ -3,6 +3,7 @@ import { AlertController } from '@ionic/angular';
 import * as Parse from 'parse';
 import { AiChatService } from './aichart.service';
 import { HttpService } from './http.service';
+import { MessageService } from './message.service';
 declare const AgoraRTC: any;
 
 @Injectable({
@@ -58,7 +59,8 @@ export class LiveService {
   constructor(
     private http: HttpService,
     private aiServ: AiChatService,
-    private alertController: AlertController
+    private alertController: AlertController,
+    private msgSer: MessageService
   ) {
     this.client?.leave();
     this.company = this.aiServ.company;
@@ -124,40 +126,28 @@ export class LiveService {
     let remoteEle = document.getElementById('vice-video');
     (remoteEle as any).style.display = 'none';
     //获取频道token记录
+    let baseurl = 'https://test.fmode.cn/api/ailiao/token';
     if (this.isAnchor) {
       this.UID = 111111;
-      let uid = Parse.User.current()?.id;
-      if (!uid) {
-        this.timer = setTimeout(() => {
-          this.getToken(room);
-        }, 2000);
-        return;
-      }
-      let baseurl = 'https://server.fmode.cn/api/webrtc/build_token';
-      let reqBody = {
-        company: this.company, // this.aiSer.company,
-        profile: room?.get('profile').id,
-        channelName: room?.get('profile').id,
-      };
-      let data: any = await this.http.httpRequst(baseurl, reqBody, 'POST');
-      console.log(data);
-      if (data.code == 200) {
-        this.options.token = data.data.token;
-        this.options.appid = data.data.appid;
-        this.options.channel = room?.get('profile').id;
-      }
+      baseurl = 'https://server.fmode.cn/api/webrtc/build_token';
+    }
+    let reqBody = {
+      company: this.company, // this.aiSer.company,
+      profile: room?.get('profile').id,
+      channelName: room?.get('profile').id,
+    };
+    let data: any = await this.http.httpRequst(baseurl, reqBody, 'POST');
+    console.log(data);
+    if (data.code == 200) {
+      this.options.token = data.data.token;
+      this.options.appid = data.data.appid;
     } else {
-      let data = await this.updateToken(room?.get('profile').id);
-      console.log(data);
-      if (!data?.token) {
-        this.timer = setTimeout(() => {
-          this.getToken(room);
-        }, 2000);
-        return;
-      }
-      this.options.token = data.token;
-      this.options.channel = data.channel;
+      this.timer = setTimeout(() => {
+        this.getToken(room);
+      }, 2000);
+      return;
     }
+    this.options.channel = room?.get('profile').id;
     this.join();
   }
   async updateToken(pid: string) {
@@ -189,7 +179,7 @@ export class LiveService {
       .then(async (uid: any) => {
         // await this.client.setClientRole('host');
         this.connection_state = this.client.connectionState;
-        console.log('进入频道当前uid:', uid,'状态:',this.connection_state);
+        console.log('进入频道当前uid:', uid, '状态:', this.connection_state);
         if (!this.isAnchor) {
           // 观众进入直播间,创建直播记录
           await this.createLiveLog(uid);
@@ -229,7 +219,7 @@ export class LiveService {
       await this.client.publish(Object.values(this.localTracks));
     } catch (err) {
       console.log('发布本地视频失败:', err);
-      this.alertTips('发布本地视频失败','提示',()=>history.back());
+      this.alertTips('发布本地视频失败', '提示', () => history.back());
     }
   }
   /* 订阅远程视频 */
@@ -297,9 +287,12 @@ export class LiveService {
         ) {
           this.timer && clearTimeout(this.timer);
           this.timer_countdown && clearInterval(this.timer_countdown);
+          if (!this.isAnchor) {
+            //用户离开直播间,断开与主播状态连接频道
+            this.msgSer.unsubscribeMessage(this.room?.get('user')?.id);
+          }
         }
         console.log('状态变更:', this.connection_state);
-        console.log(prevState);
       }
     );
     this.monitorDevices();
@@ -394,8 +387,8 @@ export class LiveService {
     console.log(data);
     this.surplusNumber = data.data ?? 0;
     this.countdown = this.surplusNumber; // 初始化倒计时
-    if(this.countdown <= 120){
-      this.alertTips('剩余通话时间不足2分钟,请及时充值')
+    if (this.countdown <= 120) {
+      this.alertTips('剩余通话时间不足2分钟,请及时充值');
     }
     let arr = ['RECONNECTING', 'DISCONNECTED', 'DISCONNECTING'];
     if (!arr.includes(this.connection_state as any)) {

+ 102 - 33
projects/live-app/src/services/message.service.ts

@@ -1,14 +1,18 @@
 import { Injectable } from '@angular/core';
 import { HttpService } from './http.service';
-import { LiveService } from './live.service';
 import * as Parse from 'parse';
 import { AlertController, ToastController } from '@ionic/angular';
 // import AgoraRTM from 'agora-rtmClient';
+import { Subject } from 'rxjs';
+import { Router } from '@angular/router';
 declare const AgoraRTM: any;
 @Injectable({
   providedIn: 'root',
 })
 export class MessageService {
+  private eventSource = new Subject<any>();
+  event$ = this.eventSource.asObservable();
+  company: string = 'Qje9D4bqol';
   rtmClient: any; // RTM实例
   // rtmClientMap: any = {};
   channelNameList: any = {}; //订阅频道状态
@@ -21,9 +25,10 @@ export class MessageService {
 
   pageFun?: Function; //页面传入的方法
   alert: any; // 弹窗
+
   constructor(
-    private liveService: LiveService,
     private alertController: AlertController,
+    private router: Router,
     public toastController: ToastController,
     private http: HttpService
   ) {}
@@ -90,7 +95,7 @@ export class MessageService {
     let uid = Parse.User.current()?.id;
     let baseurl = 'https://server.fmode.cn/api/webrtc/build_token';
     let reqBody = {
-      company: this.liveService.company, // this.aiSer.company,
+      company: this.company,
       channelName: this.msChannelName,
       type: 'withrtm',
       account: uid,
@@ -119,69 +124,133 @@ export class MessageService {
   }
   /**@监听频道消息
    *@'USERCALLINVITATION': 用户通话邀请
-   *@'REFUSE': 拒绝
-   *@'CLOASE': 拒绝
+   *@'CLOASEINVITATION': 取消通话邀请
+   *@'REFUSEINVITATION_' + uid: 拒绝通话邀请
+   *@'RESPONSEINVITOIN_' + uid: 接受邀请
    */
   joinReady(channelName?: string) {
     this.rtmClient.addEventListener('message', async (event: any) => {
       console.log('接收到一条消息:', event);
+      let states = [
+        'USERCALLINVITATION',
+        'REFUSEINVITATION_' + this.userId,
+        'CLOASEINVITATION',
+        'RESPONSEINVITOIN_' + this.userId,
+      ];
       const message = JSON.parse(event.message);
       let is_self = event.publisher == this.userId;
-      let userData = await this.getUserMetadata(event.publisher);
+      console.log('自己发出的消息:', is_self, message.text);
+      if (!is_self && states.includes(message.text)) {
+        this.callPresence(message.text, event.publisher, event.channelName);
+        return;
+      }
+      this.showMessage(event);
+    });
+    this.rtmClient.addEventListener('presence', (event: any) => {
+      console.log('频道人员状态变化 ', event);
+      let is_self = event.publisher == this.userId;
+      //远端用户离开频道,主播在线状态
       if (
-        event.channelName == this.userId &&
-        message.text == 'USERCALLINVITATION' &&
-        !is_self
+        !is_self &&
+        event.eventType === 'REMOTE_LEAVE' &&
+        event.channelName === this.userId
       ) {
-        if (this.alert) return;
+        this.setConnectState(this.userId, 'ONLINE');
+      }
+    });
+    this.rtmClient.addEventListener('linkState', (event: any) => {
+      console.log('连接状态: ', event);
+    });
+  }
+
+  /* 设置频道状态 */
+  async setConnectState(channelName: string, mode: string) {
+    const channelType = 'MESSAGE';
+    const states = {
+      Mode: mode,
+    };
+    try {
+      await this.rtmClient.presence.setState(channelName, channelType, states);
+    } catch (err: any) {
+      console.log(err);
+    }
+  }
+
+  /* 呼叫事件 */
+  async callPresence(message: string, publisher: string, channelName: string) {
+    console.log(message);
+    let userData = await this.getUserMetadata(publisher);
+    let toast;
+    switch (message) {
+      case 'USERCALLINVITATION':
+        await this.setConnectState(this.userId, 'CONNECTING');
         console.log(`收到${userData.nickname.value ?? '未知用户'}通话邀请`);
         this.alert = await this.alertController.create({
           cssClass: 'my-custom-class',
           header: '通话邀请',
           message: `收到${userData.nickname.value ?? '未知用户'}通话邀请`,
+          backdropDismiss: false,
           buttons: [
             {
               text: '拒绝',
               role: 'cancel',
-              handler: (blah) => {
-                this.publishMessage('REFUSE', event.channelName);
+              handler: async (blah) => {
+                await this.setConnectState(this.userId, 'ONLINE');
+                this.publishMessage(
+                  'REFUSEINVITATION_' + publisher,
+                  channelName
+                );
               },
             },
             {
               text: '接受',
               cssClass: 'secondary',
-              handler: () => {
-                // this.sendVideoCallInvite();
+              handler: async () => {
+                this.publishMessage(
+                  'RESPONSEINVITOIN_' + publisher,
+                  channelName
+                );
+                let rid = await this.getRoom(this.userId);
+                this.router.navigate(['live/link-room/' + rid]);
               },
             },
           ],
         });
         await this.alert.present();
-        return;
-      } else if (
-        event.channelName == this.userId &&
-        message.text == 'CLOASE' &&
-        !is_self
-      ) {
+        break;
+      case 'CLOASEINVITATION':
+        await this.setConnectState(this.userId, 'ONLINE');
         console.log(`${userData.nickname.value ?? '未知用户'}取消通话`);
         this.alert?.dismiss();
-        const toast = await this.toastController.create({
-          message: '对方已取消视频通话邀请',
+        toast = await this.toastController.create({
+          message: '对方已取消通话邀请',
           color: 'warning',
           duration: 1500,
         });
         toast.present();
-        return;
-      }
-      this.showMessage(event);
-    });
-    this.rtmClient.addEventListener('presence', (event: any) => {
-      console.log('频道人员状态变化 ', event);
-    });
-    this.rtmClient.addEventListener('linkState', (event: any) => {
-      console.log('连接状态: ', event);
-      // this.rtmClientMap[channelName].connectState = event.currentState;
-    });
+        break;
+      case 'REFUSEINVITATION_' + this.userId:
+        console.log(`${userData.nickname.value ?? '未知用户'}拒绝通话`);
+        this.alert?.dismiss();
+        this.eventSource.next(false);
+        break;
+      case 'RESPONSEINVITOIN_' + this.userId:
+        console.log(
+          `${userData.nickname.value ?? '未知用户'}同意通话,进入视频通话`
+        );
+        this.alert?.dismiss();
+        this.eventSource.next(true);
+        break;
+    }
+  }
+  async getRoom(uid: string): Promise<string | undefined> {
+    let query = new Parse.Query('Room');
+    query.equalTo('company', this.company);
+    query.equalTo('user', uid);
+    query.notEqualTo('isDeleted', true);
+    query.select('objectId');
+    let r = await query.first();
+    return r?.id;
   }
   /* 加入频道 */
   // async join(channelName: string) {