vue.jsとFirebaseのRealtime DatabaseとAuthorization機能を利用したSlackクローンの構築を複数回にわけて行っています。6回目となる今回はモーダルウィンドウを利用して作成したチャンネルを使ってメッセージの送受信を行う機能の実装を行います。

チャンネルの作成

サイドバーに表示されているチャンネルは手動で設定しているためチャンネル情報をFirebaseのデータベースに保存するためにチャンネル登録できる新たな機能を追加する必要があります。

ここではチャンネル登録用のモーダルウィンドウを作成し、チャンネル登録の機能を作ります。

モーダルウィンドウの作成

チャンネルの右にあるアイコンをクリックするとモーダルウィンドウが表示されチャンネルが登録できるように設定を行っていきます。

vue.jsでのモーダルウィドウについては下記を参考に作成しています。

チャンネルのdivタグの下にモーダルウィンドウに使用するタグを追加します。ブラウザ全体を黒の半透明で覆い、z-indexは10に設定しメッセージの画面の上の層に配置します。


<div class="font-bold opacity-50 text-lg">チャンネル</div>
<!-- modal -->
<div
  class="z-10 fixed top-0 left-0 h-full w-full flex items-center justify-center"
  style="background-color:rgba(0,0,0,0.5)"
>
  <div class="text-4xl">コンテント</div>
</div>

ブラウザで確認するとflexの設定で画面真ん中にコンテンツという文字のみ表示されます。

モーダルウィンドウの背景の設定
モーダルウィンドウの背景の設定

モーダルウィンドウの表示・非表示をデータプロパティとv-showディレクティブを利用して制御します。

vue.jsにデータプロパティchannelModalを追加します。デフォルト値はfalseとします。


data() {
  return {
    user: "",
// 略
    channelModal: false,

モーダルウィンドウのdivタグにv-showディレクティブを追加しchannelModalを設定します。アクセス直後ではchannelModalはfalseなのでブラウザ上に表示されません。


<div
  class="z-10 fixed top-0 left-0 h-full w-full flex items-center justify-center"
  style="background-color:rgba(0,0,0,0.5)"
  v-show="channelModal"
>

ブラウザで確認すると先程表示されていた黒い幕が消えます。黒い幕が消えない場合は一度ブラウザのリロードを行なってください。

デフォルトはモーダルウィンドウは表示されない
デフォルトはモーダルウィンドウは表示されない

サイドバーのチャンネルの右にあるプラスアイコンを押すとモーダルウィンドウが表示されるようにアイコンのコンポーネントにclickイベントを追加します。clickイベントにはshowChannelModalメソッドを設定します。@clickの後ろにはnativeを付けてください。コンポーネントにclickイベントを設定する場合にnativeがないと反応しません。


<PlusCircle @click.native="showChannelModal" />
‘.native’ modifier on ‘v-on’ directive is deprecatedが表示された場合はnativeを削除してください。

vue.jsにshowChannelModalメソッドを追加します。


showChannelModal() {
  this.channelModal = true;
},

設定後にチャンネルの右にあるアイコンをクリックしてください。モーダルウィンドウの黒い幕でブラウザ全体が覆われます。モーダルウィンドウを表示できるようになりましたが閉じる機能を持っていないので黒い幕をクリックすると閉じるようにモーダルウィンドウのタグにclickイベントを追加し、closeChannelModalメソッドを追加します。


<div
  class="z-10 fixed top-0 left-0 h-full w-full flex items-center justify-center"
  style="background-color:rgba(0,0,0,0.5)"
  v-show="channelModal"
  @click="closeChannelModal"
>

closeChannelModal() {
  this.channelModal = false;
}

ここまでの設定でチャンネル右のアイコンをクリックするとモーダルウィンドウが表示され、表示されたモーダルウィンドウをクリックすると非表示になることが確認できます。

モーダルウィンドの中身の作成

モーダルウィンドウの開閉の動作確認が完了したのでチャンネルの作成を行う入力フォームを先程”コンテンツ”の文字列が入っていた場所に追加します。先程のモーダルウィンドウより層が上になるz-20(z-index:20)を設定します。チャンネルの名前を入力するinput要素と作成ボタンがついているだけのシンプルな入力フォームです。classが複数設定することで幅やサイズ、スペースの調整を行っています。


//削除
<div class="text-4xl">コンテント</div>

//下記をHTMLと入れ替え
<div class="z-20 bg-white text-gray-900 w-1/3 rounded-md">
  <div class="flex flex-col p-6">
    <div class="flex justify-between items-center">
      <h2 class="text-3xl font-black leading-loose">チャンネルを作成する</h2>
      <span class="text-4xl">×</span>
    </div>
    <p>チャンネルはチームがコニュニケーションを取る場所です。特定のトピックに基づいてチャンネルを作ると良いでしょう。(例: #マーケティング)</p>
    <div class="mt-8 font-semibold">名前</div>
    <div class="my-3">
      <input
        type="text"
        class="w-full rounded border-gray-900 border-solid border p-3"
      />
    </div>
    <div class="flex justify-end">
      <button
        class="px-8 py-2 rounded bg-green-900 font-bold text-white"
      >作成</button>
    </div>
  </div>
</div>

ブラウザで追加したフォームを確認すると下記のように表示されます。

チャンネル作成の入力フォーム
チャンネル作成の入力フォーム

画面を閉じるのは作成が完了した場合とXボタンを押した場合、背景の黒い幕を押した場合に制限したいのですが現在の設定では白背景を押してもモーダルウィンドウが閉じられてしまいます。

それを防ぐために@click.stopを追加します。追加後は背景が白の部分をクリックしてもモーダルウィンドウは閉じません。


<div class="z-20 bg-white text-gray-900 w-1/3 rounded-md" v-on:click.stop>

Xボタンを押すとモーダルウィンドウが閉じるようにclickイベントを追加します。設定するメソッドは作成済みのcloseChannelModalメソッドです。


<span @click="closeChannelModal" class="text-4xl">×</span>

設定完了後モーダルウィンドウが閉じるのはXボタンと背景の黒い幕をクリックした時のみになります。

チャンネル作成のaddChannelメソッド追加

画面の作成は完了したので最後はデータベースへのチャンネル作成の処理を追加します。

input要素にバインドするデータプロパティのchannelをvue.jsに追加し、ボタンにはclickイベントでaddChannelメソッドを追加します。


<div class="my-3">
  <input
    type="text"
    class="w-full rounded border-gray-900 border-solid border p-3"
    v-model="channel"
  />
</div>
<div class="flex justify-end">
  <button
    class="px-8 py-2 rounded bg-green-900 font-bold text-white"
    @click="addChannel"
  >作成</button>
</div>

data() {
  return {
    user: "",
  //略
    channelModal: false,
    channel: "",

addChannelメソッドではチャンネルの情報はデータベースのchannelの下に保存します。保存方法はmessageの場合と同じでチャンネルを識別するためのkeyをpushから取得し、inputで入力したchannelとpushから取得したkeyを保存します。


addChannel() {
  const newChannel = firebase
    .database()
    .ref("channel")
    .push();

  const key_id = newChannel.key;

  newChannel
    .set({
      channel_name: this.channel,
      id: key_id
    })
    .then(() => {
      this.channelModal = false;
    });

  this.channel = ''
},
input要素が空だった場合や同じチャンネル名を登録しようとした場合の制御はまだ入っていません。

実際に新規のチャンネルを作成してみましょう。

サイドバーの右側にあるアイコンをクリックしてください。名前に任意の名前のチャンネル名を入力して作成ボタンをクリックしてください。

入力フォームにチャンネル名を入力
入力フォームにチャンネル名を入力

Firebaseのコンソールのデータベースから作成したチャンネルが登録されていることを確認してください。

データベースへのチャンネルの登録
データベースへのチャンネルの登録

マーケティング以外にも2つ、3つの任意の名前のチャンネルの作成を行ってみてください。

チャンネル情報の取得

取得したチャンネルをユーザと同様にライフサイクルフックのmountedでFirebaseのデータベースから取得した取得したチャンネル情報は作成済みのデータプロパティのchannelsに挿入します。

チャンネルについてはデフォルト値がこれまで入っていましが空の配列に変更してください。


data() {
  return {
    user: "",
// 略
    channelModal: false,
    channel: "",
    channels: []
  };
},

mountedのライフサイクルフックにチャンネル取得のコードを記述します。


firebase
  .database()
  .ref("channel")
  .on("child_added", snapshot => {
    this.channels.push(snapshot.val());
  });

作成したチャンネルが表示されれば正常に動作しています。

チャンネル一覧を表示
チャンネル一覧を表示

チャンネルを利用してメッセージの送受信

ダイレクトメッセージのメッセージを保存する場合は一意のchannel_idはuidの組み合わせで作成しました。チャンネルの場合はチャンネル作成時に保存したid(チャンネル作成時に自動で割り振れれるkey)を利用します。

ダイレクトメッセージと同様にサイドバーのチャンネル名をクリックしメッセージの送受信を開始します。チャンネル名を含むdivタグにclickイベントを追加し、channelMessageメソッドを追加します。


<div
  class="opacity-50 mt-1"
  v-for="channel in channels"
  :key="channel.id"
>
  <span @click="channelMessage(channel)"
    ># {{ channel.channel_name }}</span
  >
</div>

channel_idの設定方法を覗いてchannelMessageとダイレクトメッセージで作成したdirectMessageのコードは同じです。既存のチャンネルのイベントをoffメソッド削除、選択したチャンネルのchild_addedイベントでチャンネルの下にメッセージが追加されるのを監視します。


channelMessage(channel) {
  this.messages = [];
  this.channel_name = "# " + channel.channel_name;
  this.placeholder = '# ' + channel.channel_name + 'へのメッセージ';
  this.channel_id = channel.id;

  if (this.channel_id != "") {
    firebase
      .database()
      .ref("messages")
      .child(this.channel_id)
      .off();
  }

  firebase
    .database()
    .ref("messages")
    .child(channel.id)
    .on("child_added", snapshot => {
      this.messages.push(snapshot.val());
    });
},

実際にチャンネルを利用してメッセージを送受信します。

マーケティングチャンネルを使ったメッセージの送受信
マーケティングチャンネルを使ったメッセージの送受信

現時点の実装ではチャンネルに参加するユーザの制限を行っていないのでサインインできるユーザはすべてチャンネルのメッセージを閲覧することができます。

ダイレクトメッセージとは異なりチャンネル上では全員がメッセージを送信することができます。

チャンネルには全員参加できる
チャンネルには全員参加できる

チャンネルの作成、チャンネルへのメッセージの登録が可能になったので次はどのユーザが現在Slack Cloneのアプリケーションにログインしているか確認するためのログイン状態確認機能の追加を行います。