RedmineをView Customize pluginを使ってカスタマイズ～LINE風チャット画面実装編～

# 要件

- BtoCサービスのスタッフ向けに構築したRedmineで特定顧客とのメール履歴をチケットに集約した

その結果、下記のような課題がスタッフより上がってきたので、その課題の解決を`View Customize plugin`で行った。
[RedmineをView Customize pluginを使ってカスタマイズ～引用折り畳み編～](https://qiita.com/yuta-yoshinaga/items/262517c8e6cde1bd44bc)の続き。

今回LINE風のチャット画面を実装するにあたり参考にさせていただいたサイト
[CSSで作る！吹き出しデザインのサンプル19選](https://saruwakakun.com/html-css/reference/speech-bubble)

## 課題

- メールの引用部分が長すぎて、メール本文を見るのが大変
- チケットの履歴欄にメールの送受信履歴が縦に並んでいくので見づらい

## 解決案

- 引用部分はクリックで表示、非表示を切り替えできるようにする
- LINEの画面のように、相手と自分でメッセージを左右に寄せて、吹き出しの色を変える

今回は上記課題の`チケットの履歴欄にメールの送受信履歴が縦に並んでいくので見づらい`を`LINEの画面のように、相手と自分でメッセージを左右に寄せて、吹き出しの色を変える`にて解決する手順を紹介。

## 環境
- Redmine Ver. 3.4.6
- View Customize plugin Ver. 2.0.0
- テーマ Farend basic

なお、この環境のRedmineでは顧客AとスタッフAのメールやり取りが以下のような書式でRedmineのチケットの説明とノート部分に追記されていく仕様になっている。

書式としてはこんな感じ。

```
メール受信日時 : 2018/12/06 (Thu) 19:44:00
差出人 : xxx@hogehoge.com
宛先 : yyy@hogehoge.jp

顧客A様

こんばんは。カスタマーサービス東京店のスタッフAと申します。

この度はご予約をしていただきありがとうございます。
～
中略
～
長くなりましたが、よろしくお願いいたします。
またご不明な点がございましたら、お気軽にお問い合わせくださいませ。

お返事お待ちしております。

カスタマーサービス東京店
スタッフA
```

今回は上記書式メッセージをJSで抽出して、LINE風のチャット画面を構築する。

## View Customize pluginでの設定手順

[View Customize plugin](https://github.com/onozaty/redmine-view-customize)のインストール方法など公式リポジトリを参照。
以下はView Customize pluginがインストールされている前提での設定手順

1. Redmineの管理ボタンを押す
2. 日本語環境であれば`表示のカスタマイズ`を押す。
3. `新しい表示のカスタマイズ`を押す
4. パスのパターンに`/issues/*`を入力する。
5. 挿入位置を`全てのページのヘッダ`にする。
6. 種別を`CSS`にする。
7. コードに下記コードを入力する。

```CSS
/*以下、①背景色など*/
.line-bc {
  padding: 20px 10px;
  margin: 15px auto;
  background: #7da4cd;
}

/*以下、②左側のコメント*/
.balloon6 {
    width: 100%;
    margin: 10px 0;
    overflow: hidden;
}

.balloon6 .faceicon {
    float: left;
    margin-right: -50px;
    width: 40px;
}

.balloon6 .faceicon img{
    width: 100%;
    height: auto;
    border-radius: 50%;
}
.balloon6 .chatting {
    width: 100%;
    text-align: left;
}
.says {
    display: inline-block;
    position: relative; 
    margin: 0 0 0 50px;
    padding: 10px;
    max-width: 75%;
    border-radius: 12px;
    background: #edf1ee;
}

.says:after {
    content: "";
    display: inline-block;
    position: absolute;
    top: 3px; 
    left: -19px;
    border: 8px solid transparent;
    border-right: 18px solid #edf1ee;
    -ms-transform: rotate(35deg);
    -webkit-transform: rotate(35deg);
    transform: rotate(35deg);
}
.says span {
    margin: 0;
    padding: 0;
}

/*以下、③右側の緑コメント*/
.mycomment {
    margin: 10px 0;
    text-align: right;
}
.mycomment span{
    display: inline-block;
    position: relative; 
    margin: 0 10px 0 0;
    padding: 8px;
    max-width: 75%;
    border-radius: 12px;
    background: #30e852;
    /*font-size: 15px;*/
    text-align: left;
}

.mycomment span:after {
    content: "";
    position: absolute;
    top: 3px; 
    right: -19px;
    border: 8px solid transparent;
    border-left: 18px solid #30e852;
    -ms-transform: rotate(-35deg);
    -webkit-transform: rotate(-35deg);
    transform: rotate(-35deg);
}
```

8. `新しい表示のカスタマイズ`を押す
9. パスのパターンに`/issues/*`を入力する。
10. 挿入位置を`全てのページのヘッダ`にする。
11. 種別を`JavaScript`にする。
11. コードに下記コードを入力する。

```javascript
$(document).ready(function () {
    var srcHtml = "\n\t\t<!--\u2460LINE\u4F1A\u8A71\u5168\u4F53\u3092\u56F2\u3046-->\n\t\t<div class=\"line-bc\">\n\t\t</div>\n\t\t<!--/\u2460LINE\u4F1A\u8A71\u7D42\u4E86-->\n\t";
    var leftSrc = "\n\t\t<!--\u2461\u5DE6\u30B3\u30E1\u30F3\u30C8\u59CB-->\n\t\t<div class=\"balloon6\">\n\t\t\t<div class=\"chatting\">\n\t\t\t\t<div class=\"says\">\n\t\t\t\t\t<span></span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<!--\u2461/\u5DE6\u30B3\u30E1\u30F3\u30C8\u7D42-->\n\t";
    var rightSrc = "\n\t\t<!--\u2462\u53F3\u30B3\u30E1\u30F3\u30C8\u59CB-->\n\t\t<div class=\"mycomment\">\n\t\t\t<span></span>\n\t\t</div>\n\t\t<!--/\u2462\u53F3\u30B3\u30E1\u30F3\u30C8\u7D42-->\n\t";
    var lineEle = $(srcHtml);
    var cnt1 = 0;
    var cnt2 = 0;
    // 説明吹き出し化
    var desc = $('.description .wiki').html();
    var tmpStr = String(desc);
    var text = tmpStr.match(/差出人 : [\s\S]*宛先 : /);
    var from = text ? text[0].replace('差出人 : ', '').replace('宛先 : ', '').trim() : '';
    var ownerDomain = '@hogehoge.com';
    if (from != '') {
        var tgtEle = '';
        tgtEle = $(leftSrc);
        if (from.indexOf(ownerDomain) != -1) {
            // 差出人が店舗
            tgtEle = $(rightSrc);
        }
        else {
            // 差出人が顧客
            tgtEle = $(leftSrc);
        }
        if (tmpStr.indexOf('<pre>') != -1) {
            tmpStr = tmpStr.replace('<pre>', '').replace('</pre>', '').replace(/\r?\n/g, '<br>');
        }
        tgtEle.find('span').html(tmpStr);
        lineEle.append(tgtEle);
        cnt1++;
    }
    // note吹き出し化
    $('#history .wiki').each(function (index, element) {
        var desc = $(this).html();
        var tmpStr = String(desc);
        var text = tmpStr.match(/差出人 : [\s\S]*宛先 : /);
        var from = text ? text[0].replace('差出人 : ', '').replace('宛先 : ', '').trim() : '';
        if (from != '') {
            var tgtEle = '';
            tgtEle = $(leftSrc);
            if (from.indexOf(ownerDomain) != -1) {
                // 差出人が店舗
                tgtEle = $(rightSrc);
            }
            else {
                // 差出人が顧客
                tgtEle = $(leftSrc);
            }
            if (tmpStr.indexOf('<pre>') != -1) {
                tmpStr = tmpStr.replace('<pre>', '').replace('</pre>', '').replace(/\r?\n/g, '<br>');
            }
            tgtEle.find('span').html(tmpStr);
            tgtEle.find('.contextual').remove();
            lineEle.append(tgtEle);
            cnt2++;
        }
    });
    if (cnt1 != 0) {
        // 説明の下に追記
        $('.description .wiki').append(lineEle);
    }
    else if (cnt2 != 0) {
        // 履歴の下に追記
        $('#history').prepend(lineEle);
    }
});
```

※htmlソースはTypeScriptで書いて、`で囲ってヒアドキュメントにしたものをJSにトランスパイルしている。

srcHtmlの内容は参考サイトを参考に下記コードで構築した。

```html
<!--①LINE会話全体を囲う-->
<div class="line-bc">
</div>
<!--/①LINE会話終了-->
```

leftSrcの内容は参考サイトを参考に下記コードで構築した。

```html
<!--②左コメント始-->
<div class="balloon6">
	<div class="chatting">
		<div class="says">
			<span></span>
		</div>
	</div>
</div>
<!--②/左コメント終-->
```

rightSrcの内容は参考サイトを参考に下記コードで構築した。

```html
<!--③右コメント始-->
<div class="mycomment">
	<span></span>
</div>
<!--/③右コメント終-->
```

## 完成イメージ
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/291912/b7668c7f-eb56-5d6b-a876-625bda2acf45.png)

## 解説

変数`ownerDomain`にスタッフが使用するメアドのドメインを指定して、メールの方向が送信か受信かを判別している。
受信なら左側の吹き出しコードにメール本文を挿入、送信なら右側の吹き出しにメール本文を挿入している。

## 所感

ざっと書き上げたので、関数化すべきだったりする冗長な個所が多々残っている。
公開するのはお恥ずかしいが、作業着手から完成まで2時間くらいなのでご容赦ください。
LINE世代には受けが良いと思うのですが、リリースしたてで、まだフィードバックはもらってません。