Explorar el Código

用户通话邀请

warrior hace 4 meses
padre
commit
563efcbfc5

+ 1 - 1
projects/live-app/src/moduls/live/chat/chat.component.ts

@@ -79,7 +79,7 @@ export class ChatComponent implements OnInit {
     loading.present();
     await this.getProfile();
     await this.msgServe.initRTM(this.uid)
-    await this.msgServe.subscribeMessage(this.uid,{message:true}) //订阅消息
+    await this.msgServe.subscribeMessage(this.uid,{message:true,presence:true}) //订阅消息
     this.initEmoji();
     loading.dismiss();
   }

+ 0 - 1
projects/live-app/src/moduls/tabs/home/home.component.html

@@ -105,7 +105,6 @@
   </div>
 </ion-content>
 <ion-modal
-  #modal
   trigger="open-modal"
   [isOpen]="isOpen"
   (didDismiss)="onDidDismiss($event)"

+ 50 - 12
projects/live-app/src/moduls/tabs/home/home.component.ts

@@ -1,13 +1,14 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import * as Parse from 'parse';
 import {
   AlertController,
   IonicModule,
   LoadingController,
 } from '@ionic/angular';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { Swiper } from 'swiper';
 import { AiChatService } from '../../../services/aichart.service';
+import { ConnectTaskService } from '../../../services/connectTask.service';
 
 @Component({
   selector: 'app-home',
@@ -18,7 +19,6 @@ import { AiChatService } from '../../../services/aichart.service';
   // schemas: [CUSTOM_ELEMENTS_SCHEMA],
 })
 export class HomeComponent implements OnInit {
-  @ViewChild('modal') modal!: any;
   options: Array<any> = [
     {
       label: '关注',
@@ -94,12 +94,16 @@ export class HomeComponent implements OnInit {
   constructor(
     private loadingCtrl: LoadingController,
     private alertController: AlertController,
+    private activateRoute: ActivatedRoute,
+    private connectTask: ConnectTaskService,
     private router: Router,
     private aiServ: AiChatService
   ) {}
 
   ngOnInit() {
+    // this.activateRoute.paramMap.subscribe(async (params) => {
     this.refresh();
+    // });
   }
   async refresh() {
     await this.getBanner();
@@ -150,27 +154,57 @@ export class HomeComponent implements OnInit {
     });
     loading.present();
     let data: Array<any> = [];
+    if (type == this.currentValue) {
+      return;
+    }
     if (!type) type = this.currentValue;
     let uid = Parse.User.current()?.id;
     let sex = this.viewAnchor == 'all' ? null : this.viewAnchor;
+    const userList = this.connectTask.onlineUserList;
+    console.log(userList);
     switch (type) {
       case 'follow':
-        data = await this.aiServ.getRooms({ uid: uid, follow: true, sex });
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          users: userList,
+          follow: true,
+          sex,
+        });
         break;
       case 'recommend':
-        data = await this.aiServ.getRooms({ uid: uid, recommend: true, sex });
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          users: userList,
+          recommend: true,
+          sex,
+        });
         break;
       case 'news':
-        data = await this.aiServ.getRooms({ uid: uid, sex });
+        data = await this.aiServ.getRooms({ uid: uid, users: userList, sex });
         break;
       case '三星':
-        data = await this.aiServ.getRooms({ uid: uid, star: '三星', sex });
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          users: userList,
+          star: '三星',
+          sex,
+        });
         break;
       case '四星':
-        data = await this.aiServ.getRooms({ uid: uid, star: '四星', sex });
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          users: userList,
+          star: '四星',
+          sex,
+        });
         break;
       case '五星':
-        data = await this.aiServ.getRooms({ uid: uid, star: '五星', sex });
+        data = await this.aiServ.getRooms({
+          uid: uid,
+          users: userList,
+          star: '五星',
+          sex,
+        });
         break;
       default:
         break;
@@ -204,16 +238,20 @@ export class HomeComponent implements OnInit {
     // console.log(type, value);
     if (type == 'cancel') {
       this.currentValue = this.oldCurrentValue;
+      this.isOpen = false;
+      return;
     } else {
+      if (this.oldCurrentValue != this.currentValue) {
+        this.getRoom();
+      }
       this.oldCurrentValue = this.currentValue;
+      this.isOpen = false;
     }
-    this.isOpen = false;
-    this.modal.dismiss();
-    this.getRoom();
   }
   onChangeSex(e: any) {
     // console.log(e.detail.value);
     localStorage.setItem('viewSex', e.detail.value);
+    if (e.detail.value == this.viewAnchor) return;
     this.viewAnchor = e.detail.value;
     this.getRoom();
   }

+ 6 - 2
projects/live-app/src/moduls/tabs/my/my.component.ts

@@ -8,7 +8,7 @@ import {
 import * as Parse from 'parse';
 import { AgreementComponent } from '../../login/agreement/agreement.component';
 import { AlertController, ModalController } from '@ionic/angular';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { AuthService } from '../../../services/auth.service';
 import { AiChatService } from '../../../services/aichart.service';
 @Component({
@@ -27,6 +27,7 @@ export class MyComponent implements OnInit {
     private alertController: AlertController,
     public authServ: AuthService,
     public toastController: ToastController,
+    private activateRoute: ActivatedRoute,
     private router: Router,
     private aiServ: AiChatService
   ) {}
@@ -61,9 +62,12 @@ export class MyComponent implements OnInit {
     friendly_degree: 0,
   };
   ngOnInit() {
-    this.refresh();
+    this.activateRoute.paramMap.subscribe(async (params) => {
+      this.refresh();
+    });
   }
   async refresh() {
+    this.user = Parse.User.current()!;
     this.getProfile();
     this.getAgreement();
     const data = await this.aiServ.getFansAndFollow(this.user.id);

+ 13 - 11
projects/live-app/src/moduls/tabs/tabs/tabs.component.ts

@@ -1,6 +1,6 @@
 import { Component, ViewChild, Input } from '@angular/core';
 import { ChangeDetectorRef } from '@angular/core';
-import { Router, RouterOutlet } from '@angular/router';
+import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
 import * as Parse from 'parse';
 import { IonicModule } from '@ionic/angular';
 
@@ -24,10 +24,14 @@ export class TabsComponent implements OnInit {
   constructor(
     private router: Router,
     private alertController: AlertController,
+    private activateRoute: ActivatedRoute,
     private connectTask: ConnectTaskService
   ) {
-    this.active = Number(localStorage.getItem('active')) || 0
-    this.presentAlert();
+    this.activateRoute.paramMap.subscribe(async (params) => {
+      this.active = Number(localStorage.getItem('active')) || 0;
+      await this.presentAlert();
+      this.connectTask.init();
+    });
   }
 
   option: any = [
@@ -65,15 +69,13 @@ export class TabsComponent implements OnInit {
   show: any;
   active: number = 0;
 
-  ngOnInit() {
-
-  }
+  ngOnInit() {}
 
   async presentAlert() {
-    let profile = localStorage.getItem('profile');
-    if (profile) {
-      return;
-    }
+    // let profile = localStorage.getItem('profile');
+    // if (profile) {
+    //   return;
+    // }
     let uid = Parse.User.current()?.id;
     let query = new Parse.Query('Profile');
     query.equalTo('user', uid);
@@ -115,7 +117,7 @@ export class TabsComponent implements OnInit {
       return;
     }
     this.active = index;
-    localStorage.setItem('active',String(this.active))
+    localStorage.setItem('active', String(this.active));
     if (('temp' + route).indexOf('http') > 0) {
       window.location.href = route;
       console.log('temp' + route);

+ 15 - 0
projects/live-app/src/moduls/user/profile/profile.component.html

@@ -182,3 +182,18 @@
   (sendEmit)="onSendGift()"
   [toUid]="this.uid"
 ></app-gift-modal>
+@if (iscall) {
+  <div class="loading-box">
+    <div class="animation-loading">
+      <div class="container">
+        <span></span>
+        <span></span>
+        <span></span>
+        <span></span>
+      </div>
+    </div>
+    <div class="loading-title">正在呼叫</div>
+    <div class="close" (click)="onCloseCall()">取消</div>
+  </div>
+}
+

+ 108 - 22
projects/live-app/src/moduls/user/profile/profile.component.scss

@@ -124,7 +124,7 @@
   .user-data {
     width: 100%;
     padding: 6px 10px;
-    .tabs{
+    .tabs {
       display: flex;
       justify-content: space-evenly;
       margin: 10px auto;
@@ -144,16 +144,16 @@
       border-bottom: 2px solid rgb(0 0 0 / 98%);
       border-radius: 10px;
     }
-    .data-row{
+    .data-row {
       margin-top: 10px;
-      .title-text{
+      .title-text {
         font-size: 14px;
         font-weight: bold;
       }
-      .tags{
+      .tags {
         display: flex;
         flex-wrap: wrap;
-        .label{
+        .label {
           display: inline-block;
           background: #ff7378;
           padding: 2px 10px;
@@ -162,28 +162,28 @@
           margin: 6px 6px 6px 0;
           color: white;
         }
-        .tag{
-          background: #FFFFFF;
+        .tag {
+          background: #ffffff;
           border-radius: 4px;
-          border: 1px solid ;
+          border: 1px solid;
           color: #7045ff;
         }
-        .assess{
-          background: #FFFFFF;
+        .assess {
+          background: #ffffff;
           border-radius: 4px;
-          border: 1px solid ;
+          border: 1px solid;
           color: #7045ff;
         }
       }
-      .motto{
+      .motto {
         font-size: 12px;
       }
-      .gift{
+      .gift {
         display: grid;
         grid-template-columns: repeat(5, 70px);
         justify-content: space-between;
         flex-wrap: wrap;
-        img{
+        img {
           // width: 40px;
           height: 70px;
           border-radius: 50%;
@@ -191,12 +191,12 @@
           margin-top: 10px;
         }
       }
-      .album{
+      .album {
         display: grid;
         grid-template-columns: repeat(5, 70px);
         justify-content: space-between;
         flex-wrap: wrap;
-        img{
+        img {
           // width: 40px;
           height: 70px;
           object-fit: cover;
@@ -220,12 +220,12 @@
       width: 120px;
       height: 40px;
       justify-content: center;
-      ion-icon{
+      ion-icon {
         font-size: 20px;
         margin-right: 10px;
       }
     }
-    .gift{
+    .gift {
       border-radius: 50%;
       display: flex;
       align-items: center;
@@ -234,7 +234,7 @@
       width: 40px;
       height: 40px;
       color: #fe454e;
-      ion-icon{
+      ion-icon {
         font-size: 30px;
       }
     }
@@ -244,11 +244,97 @@ ion-modal {
   --height: 50%;
   --width: 350px;
   --border-radius: 16px;
-  --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+  --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1),
+    0 4px 6px -4px rgb(0 0 0 / 0.1);
 }
-.wrapper{
+.wrapper {
   height: 100%;
   display: flex;
   align-items: center;
   justify-content: center;
-}
+}
+.loading-box {
+  background: rgb(0 0 0 /90%);
+  color: white;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  position: fixed;
+  top: 0;
+  z-index: 99;
+  width: 100%;
+  height: 100%;
+  .animation-loading {
+    height: 150px;
+    width: 100%;
+    position: relative;
+  }
+  .container {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    border-radius: 50%;
+    height: 96px;
+    width: 96px;
+    animation: rotate_3922 1.2s linear infinite;
+    background-color: #9b59b6;
+    background-image: linear-gradient(#9b59b6, #84cdfa, #5ad1cd);
+  }
+
+  .container span {
+    position: absolute;
+    border-radius: 50%;
+    height: 100%;
+    width: 100%;
+    background-color: #9b59b6;
+    background-image: linear-gradient(#9b59b6, #84cdfa, #5ad1cd);
+  }
+
+  .container span:nth-of-type(1) {
+    filter: blur(5px);
+  }
+
+  .container span:nth-of-type(2) {
+    filter: blur(10px);
+  }
+
+  .container span:nth-of-type(3) {
+    filter: blur(25px);
+  }
+
+  .container span:nth-of-type(4) {
+    filter: blur(50px);
+  }
+
+  .container::after {
+    content: "";
+    position: absolute;
+    top: 10px;
+    left: 10px;
+    right: 10px;
+    bottom: 10px;
+    background-color: #2dd55b;
+    border: solid 5px #ffffff;
+    border-radius: 50%;
+  }
+
+  @keyframes rotate_3922 {
+    from {
+      transform: translate(-50%, -50%) rotate(0deg);
+    }
+
+    to {
+      transform: translate(-50%, -50%) rotate(360deg);
+    }
+  }
+  .close{
+    margin-top: 150px;
+    font-size: 14px;
+    color: #004acd;
+    /* background: white; */
+    padding: 4px 20px;
+    border-radius: 6px;
+    border: 1px solid;
+  }
+}

+ 21 - 3
projects/live-app/src/moduls/user/profile/profile.component.ts

@@ -15,6 +15,7 @@ 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';
 
 @Component({
   selector: 'app-profile',
@@ -53,7 +54,7 @@ export class ProfileComponent implements OnInit {
   isOpen: boolean = false; //打开弹窗
   room?: Parse.Object;
   @ViewChild('gift') gift!: GiftModalComponent;
-
+  iscall: boolean = false;
   constructor(
     private activateRoute: ActivatedRoute,
     private router: Router,
@@ -61,7 +62,8 @@ export class ProfileComponent implements OnInit {
     public loadingCtrl: LoadingController,
     private aiChatServ: AiChatService,
     private http: HttpService,
-    private alertController: AlertController
+    private alertController: AlertController,
+    private msgSer: MessageService
   ) {}
 
   ngOnInit() {
@@ -219,13 +221,29 @@ export class ProfileComponent implements OnInit {
           text: '确定',
           cssClass: 'secondary',
           handler: () => {
-            this.router.navigate(['live/link-room/' + this.room?.id]);
+            this.sendVideoCallInvite();
           },
         },
       ],
     });
     await alert.present();
   }
+  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!);
+  }
+  async onCloseCall() {
+    await this.msgSer.publishMessage('CLOASE', this.user?.id!);
+    this.iscall = false;
+    const toast = await this.toastController.create({
+      message: '已取消视频通话邀请',
+      color: 'warning',
+      duration: 1500,
+    });
+    toast.present();
+  }
   onSendGift() {
     console.log('点击送出礼物');
     // this.liveService.get_duration()

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

@@ -113,41 +113,10 @@ export class AiChatService {
    * @star 星级
    * @sex 主播性别
    * */
-  async getRooms(parse?: any): Promise<any> {
-    let { uid, star, sex, title, recommend, follow } = parse;
-    let whereSearch = title
-      ? `AND (prof.name LIKE '%${title}%' OR r."title" LIKE '%${title}%')`
-      : '';
-    let whereStar = star ? `AND prof."state" = '${star}'` : '';
-    let whereSex = sex ? `AND prof."sex" = '${sex}'` : `AND prof."sex" = '女'`;
-    let whereHot = recommend ? `AND r."isHot" = TRUE` : '';
-    let sql = `SELECT r."objectId" rid,r."title",r."isHot",r."cover",r."content",
-    prof."name",prof."idcard",prof."state",prof.sex,prof.birthdate,
-    us."mobile",us."objectId" AS uid,us.nickname,
-    (
-      SELECT prordar."objectId" FROM "ProfileRadar" AS prordar
-      WHERE prordar."toUser" = us."objectId"
-      AND prordar.name = '关注'
-      AND prordar."isDeleted" IS NOT TRUE
-      AND prordar."fromUser" = '${uid}'
-      LIMIT 1
-    ) AS follow
-    FROM "Room" AS r
-    LEFT JOIN "Profile" AS prof
-    ON prof."objectId" = r."profile"
-    LEFT JOIN "_User" AS us
-    ON us."objectId" = r."user"
-    WHERE r."company" = '${this.company}'
-    AND r."isDeleted" IS NOT TRUE
-    ${whereStar}
-    ${whereSex}
-    ${whereSearch}
-    ${whereHot}
-    `;
-    if(follow){
-      sql = follow ? `SELECT * FROM (${sql}) AS tabl WHERE follow IS NOT NULL` : ''
-    }
-    const data: any = await this.http.customSQL(sql);
-    return data.data;
+  async getRooms(params?: any): Promise<any> {
+    params['company'] = this.company;
+    let baseurl = 'https://server.fmode.cn/api/ailiao/roomlist';
+    const data = await this.http.httpRequst(baseurl, params, 'POST');
+    return data?.data
   }
 }

+ 6 - 4
projects/live-app/src/services/auth.service.ts

@@ -1,13 +1,14 @@
 import { Injectable } from '@angular/core';
 import * as Parse from 'parse';
 import { Router } from '@angular/router';
+import { MessageService } from './message.service';
 @Injectable({
   providedIn: 'root',
 })
 export class AuthService {
-  company:string = 'Qje9D4bqol'
+  company: string = 'Qje9D4bqol';
   redirectUrl: string = 'tabs';
-  constructor(private router: Router) {}
+  constructor(private router: Router, private msgSer: MessageService) {}
   authMobile(
     mobile: string,
     password?: string,
@@ -32,14 +33,14 @@ export class AuthService {
               .then(async (data) => {
                 // console.log(data);
                 this.redirectUrl = this.redirectUrl ? this.redirectUrl : 'tabs';
-                this.router.navigate([this.redirectUrl])
+                this.router.navigate([this.redirectUrl]);
                 resolve(data);
               })
               .catch((err) => {
                 console.error(err);
                 reject('登录失败,token已过期');
               });
-          }else{
+          } else {
             reject(authData.message);
           }
         })
@@ -57,6 +58,7 @@ export class AuthService {
   logout(): void {
     localStorage.clear(); // 清楚过期登录状态,重新登陆
     Parse.User.logOut().then((user) => {
+      this.msgSer.logOutRTM();
       this.router.navigate(['login']);
     });
   }

+ 85 - 3
projects/live-app/src/services/connectTask.service.ts

@@ -1,11 +1,93 @@
 import { Injectable } from '@angular/core';
 import { MessageService } from './message.service';
+import * as Parse from 'parse';
 @Injectable({
   providedIn: 'root',
 })
 export class ConnectTaskService {
-  msChannelName: string = 'user_connect_room'; // 保存用户连接状态
-  constructor(private msgSer: MessageService) {
-    this.msgSer.initRTM(this.msChannelName);
+  anchorChannelName?: string;
+  msChannelName: string = 'user_connect_room'; // 主播在线上报频道
+  onlineUserList: Array<string> = []; // 在线用户列表
+  constructor(private msgSer: MessageService) {}
+
+  async init() {
+    // 初始化消息服务,所有用户静默登录
+    await this.msgSer.initRTM(this.msChannelName);
+    await this.anchorOnline();
+    this.getOnlieUserList(this.msChannelName, 'MESSAGE');
+  }
+  /* 主播上线 */
+  async anchorOnline() {
+    let profile = JSON.parse(localStorage.getItem('profile') || '');
+    const uid = Parse.User.current()?.id!;
+    if (profile?.identyType == 'anchor') {
+      let nowChannes = await this.getWhereNow(uid);
+      console.log('用户已订阅频道:', nowChannes);
+      // if (!nowChannes.includes(this.msChannelName)) {
+        console.log('订阅成功');
+        /* 主播订阅主播频道 */
+        this.msgSer.subscribeMessage(this.msChannelName); //主播开启并订阅自己的聊天频道
+      // }
+      // if (!nowChannes.includes(uid)) {
+        this.msgSer.subscribeMessage(uid, { message: true }); //开启并订阅自己的聊天频道
+      // }
+    }
+  }
+
+  /* 获取用户当前所在频道 */
+  async getWhereNow(userId: string): Promise<Array<string>> {
+    let channes: Array<string> = [];
+    try {
+      const whereNowResult = await this.msgSer.rtmClient.presence.whereNow(
+        userId
+      );
+      const { channels, totalChannel } = whereNowResult;
+      channels.forEach((channelInfo: any) => {
+        const { channelName, channelType } = channelInfo;
+        channes.push(channelName);
+      });
+      return channes;
+    } catch (status: any) {
+      const { operation, reason, errorCode } = status;
+      console.error(
+        `${operation} failed, the error code is ${errorCode}, because of: ${reason}.`
+      );
+      return [];
+    }
+  }
+
+  /* 获取在线用户列表 */
+  async getOnlieUserList(
+    channelName: string,
+    page?: string,
+    channelType?: string
+  ) {
+    if (this.onlineUserList.length > 0) return;
+    console.log('获取在线用户列表');
+    const options: any = {
+      includedUserId: true,
+      includedState: true,
+    };
+    if(page) options.page = page;
+    try {
+      const result = await this.msgSer.rtmClient.presence.whoNow(
+        channelName,
+        channelType ?? 'MESSAGE',
+        options
+      );
+      // console.log(result);
+      // 如果 nextPage 存在,下一次调用 whoNow 时,需将 nextPage 的值填入 whoNowOptions 的 page 字段
+      const { totalOccupancy, occupants, nextPage } = result;
+      occupants.forEach((userInfo: any) => {
+        const { states, userId, statesCount } = userInfo;
+        this.onlineUserList.push(userId);
+      });
+      if (nextPage) this.getOnlieUserList(channelName, nextPage, channelType);
+    } catch (status: any) {
+      const { operation, reason, errorCode } = status;
+      console.error(
+        `${operation} failed, ErrorCode: ${errorCode}, due to: ${reason}.`
+      );
+    }
   }
 }

+ 98 - 47
projects/live-app/src/services/message.service.ts

@@ -2,6 +2,7 @@ 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';
 declare const AgoraRTM: any;
 @Injectable({
@@ -19,9 +20,13 @@ export class MessageService {
   userId: string = Parse.User.current()?.id!;
 
   pageFun?: Function; //页面传入的方法
-  constructor(private liveService: LiveService, private http: HttpService) {
-    // this.initRTM(this.msChannelName);
-  }
+  alert: any; // 弹窗
+  constructor(
+    private liveService: LiveService,
+    private alertController: AlertController,
+    public toastController: ToastController,
+    private http: HttpService
+  ) {}
   messageMapList: any = {
     global_room: [
       // 世界频道消息列表
@@ -80,6 +85,7 @@ export class MessageService {
 
   /* 获取token */
   async getToken(channel?: string) {
+    this.userId = Parse.User.current()?.id!;
     //获取频道token记录
     let uid = Parse.User.current()?.id;
     let baseurl = 'https://server.fmode.cn/api/webrtc/build_token';
@@ -102,34 +108,75 @@ export class MessageService {
 
   async initRTM(channelName: string) {
     // let states = ['CONNECTED', 'CONNECTING'];
-    //已连接,无需重复连接
-    // if (
-    //   this.rtmClientMap?.[channelName]?.connectState &&
-    //   states.includes(this.rtmClientMap[channelName].connectState)
-    // )
-    //   return;
-    // console.log('initRTM');
-
     if (this.options.connectState) return;
     await this.getToken(channelName);
+    // const rtmConfig = { logLevel: 'debug' };
     this.rtmClient = new AgoraRTM.RTM(this.appid, this.userId);
-    // this.rtmClientMap[channelName] = {
-    //   rtmClient: new AgoraRTM.RTM(this.appid, this.userId),
-    // };
     this.joinReady();
     await this.loginRTM();
     // this.subscribeMessage(channelName);
-    this.publishMessage('user-online',channelName,'USER')//用户上线通知
+    // this.publishMessage('user-online', channelName, 'USER'); //用户上线通知
   }
-  /* 监听频道消息 */
+  /**@监听频道消息
+   *@'USERCALLINVITATION': 用户通话邀请
+   *@'REFUSE': 拒绝
+   *@'CLOASE': 拒绝
+   */
   joinReady(channelName?: string) {
-    this.rtmClient.addEventListener('message', (event: any) => {
-      console.log('message', event);
+    this.rtmClient.addEventListener('message', async (event: any) => {
+      console.log('接收到一条消息:', event);
+      const message = JSON.parse(event.message);
+      let is_self = event.publisher == this.userId;
+      let userData = await this.getUserMetadata(event.publisher);
+      if (
+        event.channelName == this.userId &&
+        message.text == 'USERCALLINVITATION' &&
+        !is_self
+      ) {
+        if (this.alert) return;
+        console.log(`收到${userData.nickname.value ?? '未知用户'}通话邀请`);
+        this.alert = await this.alertController.create({
+          cssClass: 'my-custom-class',
+          header: '通话邀请',
+          message: `收到${userData.nickname.value ?? '未知用户'}通话邀请`,
+          buttons: [
+            {
+              text: '拒绝',
+              role: 'cancel',
+              handler: (blah) => {
+                this.publishMessage('REFUSE', event.channelName);
+              },
+            },
+            {
+              text: '接受',
+              cssClass: 'secondary',
+              handler: () => {
+                // this.sendVideoCallInvite();
+              },
+            },
+          ],
+        });
+        await this.alert.present();
+        return;
+      } else if (
+        event.channelName == this.userId &&
+        message.text == 'CLOASE' &&
+        !is_self
+      ) {
+        console.log(`${userData.nickname.value ?? '未知用户'}取消通话`);
+        this.alert?.dismiss();
+        const 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.pageFun?.();
     });
     this.rtmClient.addEventListener('linkState', (event: any) => {
       console.log('连接状态: ', event);
@@ -199,23 +246,28 @@ export class MessageService {
   /* 订阅消息 */
   subscribeMessage(channelName: string, param?: any, callback?: any) {
     if (this.channelNameList[channelName]) return;
-    const options = {
-      withMessage: param?.message ?? false, // message 事件
-      withPresence: false, // presence 事件
-      beQuiet: false, // quiet 事件
-      withMetadata: false, // metadata 事件
-      withLock: false, // lock 事件
-    };
-    this.rtmClient
-      .subscribe(channelName, options)
-      .then((res: any) => {
-        console.log('subscribeMessage', res);
-        //订阅成功
-        this.channelNameList[channelName] = true;
-      })
-      .catch((err: any) => {
-        console.error('subscribeMessageErr', err);
-      });
+    return new Promise((resolve, reject) => {
+      const options = {
+        withMessage: param?.message ?? false, // message 事件
+        withPresence: param?.presence ?? false, // presence 事件
+        beQuiet: false, // quiet 事件
+        withMetadata: false, // metadata 事件
+        withLock: false, // lock 事件
+      };
+      this.rtmClient
+        .subscribe(channelName, options)
+        .then((res: any) => {
+          console.log('subscribeMessage', res);
+          //订阅成功
+          this.channelNameList[channelName] = true;
+          this.pageFun?.();
+          resolve(true);
+        })
+        .catch((err: any) => {
+          console.error('subscribeMessageErr', err);
+          reject(false);
+        });
+    });
   }
   /* 取消订阅 */
   unsubscribeMessage(channelName: string) {
@@ -234,15 +286,10 @@ export class MessageService {
   async showMessage(param: any) {
     let userData = await this.getUserMetadata(param.publisher);
     let is_self = param.publisher == this.userId;
-    // let r: any = Parse.User.current()!;
-    // if (!is_self) {
-    //   let queryUser = new Parse.Query('_User');
-    //   queryUser.equalTo('objectId', param.publisher);
-    //   queryUser.select('avatar', 'nickname');
-    //   r = await queryUser.first();
-    // }
     console.log(userData);
     let message = JSON.parse(param.message);
+    if (!this.messageMapList[param.channelName])
+      this.messageMapList[param.channelName] = [];
     this.messageMapList[param.channelName].push({
       is_self: is_self,
       avatar:
@@ -250,7 +297,7 @@ export class MessageService {
         'https://file-cloud.fmode.cn/DXNgcD6zo6/20221202/j6p8kb034039.png',
       msg_type: 1,
       name: userData.nickname.value ?? '未知用户',
-      content: message?.message ?? '',
+      content: message?.text ?? '',
       create_time: new Date(param.timeStamp),
       istoday: true,
       timeStamp: new Date(param.timeStamp),
@@ -268,10 +315,14 @@ export class MessageService {
     }
   }
 
-  async publishMessage(message: string, channelName: string, channelType?:string) {
-    const payload = { type: 'text', message: message };
+  async publishMessage(
+    message: string,
+    channelName: string,
+    channelType?: string
+  ) {
+    const payload = { type: 'text', text: message };
     const publishMessage = JSON.stringify(payload);
-    const publishOptions = { channelType: 'MESSAGE' };
+    const publishOptions = { channelType: channelType ?? 'MESSAGE' };
     try {
       const result = await this.rtmClient.publish(
         channelName,