画面を更新してもチャットの履歴を表示できるようにする

リアルタイムでやり取りしたチャットメッセージは、通常一過性のもので、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はまだまだ進化の余地があるテクノロジーです。内側(メーカー)と外側(ベンダー、サードパーティー)で創造と想像を繰り返して、新しい価値を見いだしていけるといいですね。