画面を更新してもチャットの履歴を表示できるようにする
リアルタイムでやり取りしたチャットメッセージは、通常一過性のもので、Web画面を更新してしまうと消えてなくなります。幸い、この連載で扱っているチャットデータは、Notesデータベースに記録されています。この情報を再取得すれば、チャットのやり取りを画面に再現することができます。
チャットデータをリロードするタイミングは、画面のロード時になるので、onLoad時にイベントを発行します。
// public/javascripts/chat.js (function() { // クライアント側のソケットを初期化します。 var socket = io.connect('http://localhost:3000'); // (中略) // リフレッシュメッセージを受信します。 socket.on('refresh', function(data) { var range = data.range.match(/items (\d+)-(\d+)\/(\d+)/); var start = parseInt(range[1]); var end = parseInt(range[2]); var allCount = parseInt(range[3]); if (start === 0) $('#output').empty(); data.items.forEach(function(item) { var content = item['$120']; var message = content.match(/(.*) \(/); var username = content.match(/ \((.*)\)/); $('#output').prepend( '<p>' + '<strong>' + username[1] + '</strong>: ' + message[1] + '</p>' ); }); if (end + 1 < allCount) socket.emit('reload', {'start': end + 1, 'count': countUnit}); }); // チャットの履歴を取得します。 var countUnit = 10; socket.emit('reload', {'start': 0, 'count':countUnit}); })()
chat.jsで定義している無名関数がロード時に実行されるので、ここでメッセージの履歴を取得します。最後の方にある「socket.emit('reload', ...)」が、Node.jsのWebSocketに対してメッセージのリロード要求をしています。
前半部の「socket.on('refresh', ...);」については後ほど補足します。
Webクライアントからreloadメッセージが送信されると、Note,js(WebSocket)ではそのままカスタムクラス「NodeChat」の「reload」メソッドを呼び出します。
// mySocket.js // (中略) module.exports = function(server, sessionStore, User) { // (中略) // チャットの内容をリロードします。 socket.on('reload', function(input) { getUser(socket) .then(function(user) { var nodeChat = new NodeChat(user); return nodeChat.reload(input); }) .then(function(data) { socket.emit('refresh', data); }) .catch(function(err) { console.log(err); }); }); }); return io; };
NodeChat.reload処理が無事に終わると、送信元(Webクライアント)に更新データを送ります(socket.emit('refresh', data);)。それでは、肝心のNodeChat.reloadの仕組みを見ていきます。
// NodeChat.js // (中略) // 現在のチャットデータすべてを取得します。 NodeChat.prototype.reload = function(input) { var cliObj = this.client; return new Promise(function(resolve, reject) { var args = { 'parameters': { 'compact': true , 'start': input.start , 'count': input.count } }; cliObj.get( 'http://localhost/NodeChat.nsf/api/data/collections/name/($All)' , args , function(retData, response) { if (retData.code && retData.code !== 200) reject(retData); else { resolve({ items: retData , range: response.headers['content-range'] }); } } ).on('error', reject); }); } module.exports = NodeChat;
チャットデータのすべてを取得する方法はいくつか考えられますが、ここでは、Dominoデータサービスのビューエントリーをすべて取得する方法を考えます。このAPIについては以下のリンクに詳細があります。
IBM Notes and Domino Application Development wiki : IBM Domino Access Services 9.0.1
チャットデータのリロードを要求する際に、「start=0、count=countUnit(=10)」という引数を付けて呼び出しましたが、その指定値が変数「args.parameters.start」と「args.parameters.count」に反映されます。ビューから取得するエントリーの範囲を指定しているわけです。この値を持たせて、データベース「NodeChat.nsf」、ビュー「($All)」にHTTP GETを要求すると、指定したビューから指定した範囲のエントリーデータを取得できるわけです。
また、このGETリクエストのレスポンスには、「このデータは○件中○番目から○件のデータです」という情報が、ヘッダー情報の「content-range」に保存されているので、このデータもリスエスト元に返すことで、次の取得範囲を計算できるようにしてあげます。
ここでようやく「public/javascripts/chat.js」の「socket.on('refresh',~」の説明になります。
最初にしていることは、返ってきたデータのうち、rangeプロパティに含まれる件数情報を取得することです。件数情報を正規表現で分解し、start(開始インデックス)、end(終了インデックス)、allCount(全件数)に分けます。startが0ならば、初回のリクエストと言うことで、画面の出力データをクリーンアップします。2回目以降はあるデータの継ぎ足しになるわけです。
次はメッセージ情報を画面に展開していきます。「data.items」に配列としてメッセージデータが含まれているので、これを順次処理していきます。今回はビューデータの列番号「$120」のデータを元に、メッセージとその作成者を特定し、画面に展開しています。
最後に、今回の取得範囲がまだ最後でない場合、取得範囲を変えて、再度「reload」リクエストをWebSocketサーバに要求します。これを繰り返すことで、1回の要求範囲では完全ではない場合でも、非同期処理でいずれでデータが揃うということになります。
最後に、スタイルシートを一部修正します。
// public/stylesheets/style.css // (中略) div#chat-window { height: 270px; background-color: #dddddd; overflow: scroll; } // (中略)
「overflow: scroll;」を追加しました。これを追加しておかないと、チャットメッセージが増えたときに表示できなくなります。
以上で、約1ヶ月の短期連載は終了です。
DominoとNode.jsの同居サーバ構築 - Chiburu Systemsのブログ
Dominoアクセスになんちゃって認証を追加する - Chiburu Systemsのブログ
Dominoを離れてNode.jsだけでチャットの実装をしてみる - Chiburu Systemsのブログ
Node.jsのチャットデータをNSFに保存してみる - Chiburu Systemsのブログ
Notesで書いたメッセージをチャットに送る - Chiburu Systemsのブログ
この連載の間に、Notes/Dominoのバージョン10に関するニュースも飛び込んできました。Notes/Dominoはまだまだ進化の余地があるテクノロジーです。内側(メーカー)と外側(ベンダー、サードパーティー)で創造と想像を繰り返して、新しい価値を見いだしていけるといいですね。