作者:陳畏民
源起
今年寒假的前半段時(shí)間, 在家搗鼓了一個(gè)情侶類web應(yīng)用, 基于aspnetcore和angular搭建的; 寒假中實(shí)現(xiàn)了'告白', '相冊(cè)', '說說', '紀(jì)念日'這些功能, 然后前端界面上留一個(gè)功能的坑位: 聊天, 點(diǎn)擊這個(gè)聊天按鈕, 可以看到四個(gè)字, 那就是敬請(qǐng)期待; 部署上線后, 用戶當(dāng)然只有我和我的"好朋友"使用, "好朋友"先跨了我真棒, 然后問聊天功能馬上可以用了吧? 我沉默了, 心想著這個(gè)功能后面用signalr試試看吧; 現(xiàn)在已經(jīng)2020秋了, 聊天功能的界面上依舊是那四個(gè)字: 敬請(qǐng)期待
!
這個(gè)國(guó)慶, 我意識(shí)到不能再拖了, 自己埋的坑, 應(yīng)該趁早把它填了, 否則"好朋友"會(huì)覺得你很菜, 一個(gè)"簡(jiǎn)單的"聊天功能都做不出來;
遇見聲網(wǎng)(agora)
最開始想用signalr自己實(shí)現(xiàn)聊天功能的, 但是考慮到一方面, 自己的服務(wù)器資源有限(1核1G輕量應(yīng)用服務(wù)器); 另一方面, 自身精力能力有限, 寫出來也許不難, 但是要寫好確是不簡(jiǎn)單的; 于是尋思著找找現(xiàn)成的東西用用吧, 機(jī)緣巧合, 我聽說了聲網(wǎng)(agora), 于是去他的官網(wǎng)看了一番, 看到有詳細(xì)的文檔, 足量的免費(fèi)額度...于是決定先白嫖試用一下
關(guān)于agora
特地找了一下agora的相關(guān)資料, 看起來是挺靠譜的, 在全球都有數(shù)據(jù)中心和服務(wù)器; 小米、陌陌、新東方等知名企業(yè)都用過他們的云服務(wù);
基于agora的rtm sdk給我的應(yīng)用加上聊天功能
我的環(huán)境
- win10系統(tǒng)
- npm包管理
- angular8.x
- vscode
步驟
安裝依賴
npm i agora-rtm-sdk
安裝完后需要修改下agora-rtm-sdk/index.d.ts的文件的2258行
原來的內(nèi)容為:
export type { RtmChannel, RtmClient, RtmEvents, RtmMessage, RtmStatusCode };
修改為:
export { RtmChannel, RtmClient, RtmEvents, RtmMessage, RtmStatusCode };
不修改的話, 編譯會(huì)報(bào)錯(cuò)
引入依賴
因?yàn)槭窃谠赼ngular組件ChatComponent中實(shí)現(xiàn)聊天相關(guān)的功能, 所以在其中引入rtm sdk的依賴 import AgoraRTM from 'agora-rtm-sdk';
創(chuàng)建rtm客戶端并登陸到agora的rtm服務(wù)器
一行代碼創(chuàng)建rtm客戶端:
const rtmClient = AgoraRTM.createInstance('<your app id>');
<details>
<summary>登陸到rtm服務(wù)器</summary>
const rtmClient = AgoraRTM.createInstance('fd033b52ca5d40599efc96f6e2131639');
async function rtmClientLogin(user: User) {
try {
await rtmClient.login({ token: null, uid: user.id });
} catch(err) {
console.log('AgoraRTM client login failure', err);
}
}
// 在組件的 ngOnInit 方法中調(diào)用 rtmClientLogin
async ngOnInit() {
try {
let user = await this.userServ.getUser().toPromise();
if (user instanceof User) {
this.user = user;
rtmClientLogin(this.user);
} else {
throw new Error('無法獲取用戶數(shù)據(jù)');
}
} catch(err) {
this.notifyServ.error('初始化聊天組件失敗', null);
console.error('初始化聊天組件失敗', err);
}
}
ps: 測(cè)試階段, 所以使用的rtm的授權(quán)方式是AppID, 如果要使用這種授權(quán)方式, 在rtm控制臺(tái)創(chuàng)建項(xiàng)目的時(shí)候要注意一下, 身份認(rèn)證模式勾選 App ID, 否則在登陸到rtm服務(wù)器的時(shí)候, 會(huì)報(bào)紅
發(fā)送/接收消息
消息發(fā)送失敗需要通知用戶, 錯(cuò)誤通知直接使用了antdesign的NzNotificationService, 在構(gòu)造函數(shù)注入即可; 這個(gè)應(yīng)用中, 互相發(fā)消息的雙方是情侶, User表示當(dāng)前用戶, User.Spouse表示用戶的伴侶; 消息發(fā)送成功需要清空發(fā)送消息文字框并將發(fā)送的消息加入消息數(shù)組中, 讓angular更新視圖
發(fā)送消息
async sendMessage() {
if (!this.newMessage) {
return;
}
const spouseId = this.user.spouse.id;
try {
const result = await rtmClient.sendMessageToPeer({
text: this.newMessage
}, spouseId);
if (!result.hasPeerReceived) {
throw new Error('對(duì)方未接受消息');
} else {
this.messages.push({
text: this.newMessage,
sender: this.user,
receiver: this.user.spouse,
dateSended: new Date()
});
this.newMessage = undefined;
}
} catch (err) {
this.notifyServ.error('發(fā)送消息失敗', null);
console.log('發(fā)送消息失敗', err);
}
}
在ngOnInit生命周期函數(shù)中監(jiān)聽收到新消息事件, 收到新消息后, 將新消息加入消息數(shù)組中, angular會(huì)通過數(shù)據(jù)綁定更新視圖, 渲染ui
監(jiān)聽并處理收到新消息事件
async ngOnInit() {
try {
let user = await this.userServ.getUser().toPromise();
if (user instanceof User) {
this.user = user;
rtmClientLogin(this.user);
監(jiān)聽接收到消息事件
rtmClient.on('MessageFromPeer', (rtmMessage, peerId) => {
this.messages.push({
text: rtmMessage.text,
sender: this.user.spouse,
receiver: this.user,
dateSended: new Date()
});
});
} else {
throw new Error('無法獲取用戶數(shù)據(jù)');
}
} catch(err) {
this.notifyServ.error('初始化聊天組件失敗', null);
console.error('初始化聊天組件失敗', err);
}
}
前端html代碼
<div id="container">
<div class="messages">
<div class="message-item"
*ngFor="let msg of messages">
<div class="sendedMessage"
*ngIf="msg.sender.id === user.id">
<span class="message-text">{{msg.text}}</span>
<span>
<nz-avatar nzIcon="user"
[nzSrc]="msg.receiver.profileImageUrl"></nz-avatar>
</span>
</div>
<div class="receviedMessage"
*ngIf="msg.sender.id === user.spouse.id">
<span>
<nz-avatar nzIcon="user"
[nzSrc]="msg.sender.profileImageUrl"></nz-avatar>
</span>
<span class="message-text">{{msg.text}}</span>
</div>
</div>
</div>
<div class="new-message">
<div nz-row
nzJustify="end">
<div nz-col
nzSpan="18">
<textarea nz-input
[(ngModel)]="newMessage"
[nzAutosize]="{ minRows: 1, maxRows: 6 }"></textarea>
</div>
<div nz-col
nzSpan="6">
<button nz-button
nzType="primary"
class="mx-auto"
style="width: 100%;"
(click)="sendMessage()">發(fā)送</button>
</div>
</div>
</div>
</div>
前端css
:host {
height: 100%;
display: block;
position: relative;
}
#container {
height: 100%;
display: flex;
flex-direction: column;
padding-top: 4px;
}
.messages {
flex-grow: 1;
}
nz-alert {
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 85%;
text-align: center;
}
.sendedMessage {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 8px;
}
.receviedMessage {
margin-bottom: 8px;
}
.message-text {
background: #fff;
padding: 8px 4px;
}
.sendedMessage .message-text {
margin-right: 4px;
}
.receviedMessage .message-text {
margin-left: 4px;
}
效果如何?
動(dòng)圖演示:
[圖片上傳失敗...(image-4cbfbb-1614221898111)]
靜圖:
小結(jié)
上文基于agora的rtm sdk, 初步實(shí)現(xiàn)了簡(jiǎn)單的聊天功能; 體驗(yàn)下來感覺很方便, 不需要關(guān)注后端實(shí)現(xiàn), 只需要處理前端邏輯即可輕松構(gòu)建出實(shí)時(shí)聊天功能; 當(dāng)然, 正式在生產(chǎn)環(huán)境使用, 還是需要后端配合生成一個(gè)身份認(rèn)證令牌(token)來保證安全性的; 上文暫時(shí)只實(shí)現(xiàn)了文字的發(fā)送接收, 實(shí)際上rtm sdk還支持文件和圖片的收發(fā), 功能很強(qiáng)大, 有機(jī)會(huì)再繼續(xù)探索。
本文是參與聲網(wǎng)"內(nèi)容共建計(jì)劃"的開發(fā)者投稿作品
了解更多實(shí)時(shí)互動(dòng)相關(guān)內(nèi)容可點(diǎn)擊進(jìn)入聲網(wǎng)RTC 開發(fā)者社區(qū)進(jìn)行查看