NCMBとMonacaを使ってチャットアプリを作ります。以前、PieSocketを使ったチャットアプリを公開したのですが、現在○○は無料での利用枠がなくなっています。そこで今回はgoofmint/ruby-websockets-chat-demoをベースにチャットアプリを作ります。これはごく単純なWebScoketサーバーで、Herokuにデプロイできるものです。Herokuの無料枠内での利用が可能です。
前回の記事では認証周りを解説しました。今回はチャット処理について解説します。
コードについて
今回のコードはNCMBMania/MonacaChatDemoにアップロードしてあります。実装時の参考にしてください。
WebSocketサーバーのデプロイ
チャットサーバーをデプロイします。Herokuに対応していますので、クリック1つでデプロイできます。
goofmint/ruby-websockets-chat-demo
デプロイした際のURLが https://CHAR-SERVER.herokuapp.com
だとしたら、https
を wss
に変更して config.json
にて websocketUrl
キーで設定します。 wss
はWebSocketのSSL/TLS版です。
{
"applicationKey": "YOUR_APPLICATION_KEY",
"clientKey": "YOUR_CLIENT_KEY",
"websocketUrl": "wss://CHAR-SERVER.herokuapp.com"
}
WebSocket接続の設定
NCMBの初期化を行った js/app.js
にて、同じくWebSocketの初期化を行います。その際、状態を把握するのに使える関数も指定しておきます(必須ではありません)。
const webSocket = new WebSocket(config.websocketUrl);
webSocket.onopen = onOpen;
webSocket.onclose = onClose;
webSocket.onerror = onError;
window.webSocket = webSocket;
// 接続した時に呼ばれるイベント
const onOpen = (e) => {
}
// エラーが出た際に呼ばれるイベント
const onError = (e) => {
console.log(e);
};
// 接続を閉じた時に呼ばれるイベント
const onClose = (e) => {
}
チャット画面について
チャット画面はFramework7のメッセージ機能を使うので、HTMLはとてもシンプルです。
<div class="page" data-name="chat">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner sliding">
<div class="title">チャット</div>
</div>
</div>
<div class="toolbar messagebar">
<form id="chat">
<div class="toolbar-inner">
<div class="messagebar-area">
<textarea name="text" class="resizable" placeholder="Message"></textarea>
</div>
<a class="link send-link" href="#" @click=${sendMessage}>Send</a>
</div>
</form>
</div>
<div class="page-content">
<div class="messages">
</div>
</div>
</div>
変数の準備
画面を構築するのに必要な変数、NCMBのデータストアに保存する際に使う変数を準備します。
export default (props, { $on, $update, $tick, $onMounted }) => {
// データストアに保存する用のクラス名(DBで言うテーブル名相当)
const Message = ncmb.DataStore('Message');
// ログインユーザー
const user = ncmb.User.getCurrentUser();
// データ保存する際のACL(アクセス権限)
const acl = new ncmb.Acl;
acl
.setPublicReadAccess(true) // 誰でも閲覧可能
.setUserWriteAccess(user, true); // ユーザのみ編集・削除可能
let messagebar; // メッセージ入力欄用
let messages = []; // チャットメッセージ(描画用)
let messageObjects = []; // チャットメッセージ(データストアから取得)
チャットメッセージを受け取った際の処理を用意
チャットメッセージをWebSocketサーバー経由で受け取った際の処理を記述します。これはWebSocketサーバーが送信する内容を解析し、後述する addMessage
関数にデータを送ります。 unescapeHtml
関数は○○より拝借しています。
// マウントされた際に実行
$onMounted(() => {
// WebSocketでメッセージを受け取った際に呼び出される関数
window.webSocket.onmessage = (e) => {
// 文字列のメッセージをJSONとしてパース
const message = JSON.parse(e.data);
// ユーザー情報はHTMLをアンエスケープした上でパース
message.user = JSON.parse(unescapeHtml(message.user));
// チャットメッセージを追加
addMessage(message);
};
});
// HTMLをアンエスケープする関数
const unescapeHtml = (target) => {
if (typeof target !== 'string') return target;
const patterns = {
'<': '<',
'>': '>',
'&': '&',
'"': '"',
''': '\'',
'`': '`'
};
return target.replace(/&(lt|gt|amp|quot|#x27|#x60);/g, match => {
return patterns[match];
});
};
画面の初期化処理
画面が初期化された際には page:init
イベントが呼ばれます。ここでFramework7のメッセージ機能を利用します。処理がちょっと長いですが、コメントを参照してください。
// ページが初期化される際に呼ばれる関数
$on('page:init', async () => {
// チャットメッセージの作成
messages = app.messages.create({
el: '.messages',
scrollMessagesOnEdge: true,
// 最後のメッセージか判定する関数
lastMessageRule: function (message, previousMessage, nextMessage) {
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
},
// メッセージに吹き出しを付けるか判定する関数
tailMessageRule: function (message, previousMessage, nextMessage) {
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
}
});
// データストアからメッセージを取得
messageObjects = await getMessages();
// チャットメッセージに追加
messageObjects.forEach(addMessage);
// メッセージ入力欄を作成
messagebar = app.messagebar.create({
el: '.messagebar'
});
});
メッセージの取得
getMessages
関数はNCMBのデータストアから既存メッセージを取得する処理です。
// NCMBにあるチャットメッセージを取得する関数
const getMessages = () => {
return Message
.include('user') // ユーザー情報も取得
.limit(100) // 100件まで
.fetchAll();
}
メッセージの追加処理
取得したメッセージは addMessage
関数にて messages
に追加していきます。この関数はWebSocketサーバーからメッセージを受け取った際にも利用されます。
// チャット表示用のメッセージを追加する関数
const addMessage = (message) => {
// 詳細は以下を参照
// https://framework7.io/docs/messages#single-message-parameters
messages.addMessage({
text: message.message,
type: message.user.objectId === user.objectId ? 'sent' : 'received',
name: message.displayName,
avatar: '/assets/icons/person.png',
});
};
メッセージの送信
メッセージを送信すると sendMessage
関数が呼ばれます。ここではNCMBのMessageクラスにデータを保存した上で、メッセージをWebSocketサーバーに送信しています。WebSocketでは文字列のみ扱えますので、JSON.stringifyを使って文字列化しています。
// メッセージ送信処理
const sendMessage = async () => {
// 入力内容を取得
const params = app.form.convertToData($('#chat'));
// Messageクラスのインスタンス(DBでいう行相当)を作成
const message = new Message;
// データを適用して保存
await message
.set('user', user)
.set('acl', acl)
.set('message', params.text)
.save();
// WebSocketで通知
window.webSocket.send(JSON.stringify(message));
}
これでチャットアプリの完成です。
まとめ
今回はチャット画面周りを解説しました。WebSocketサーバーを自分で立てれば、簡単なチャットアプリを作成できます。WebSocketサーバーだけではメッセージが保存されませんが、NCMBのデータストアを組み合わせることで、チャットメッセージを残しておけます。
Framework7のメッセージ機能は画像を投稿、表示できるなど多機能なものになっています。今回のチャットアプリをカスタマイズして実装してみてください。