もともと nw.js で作っていたのですが、久々に Electron を見てみたらトレイ周りがかっちょよくなっていたので、書きなおしてみました(昔ぼくが見た時はトレイを表示させるとベコベコ感が気に入らなくて、nw.js だったのですが勘違したのかも?)。
使った npm パッケージ
{
"dependencies": {
"lodash": "^3.10.1",
"moment": "^2.10.6",
"q": "^1.4.1",
"react": "^0.13.3",
"request": "^2.61.0"
},
"devDependencies": {
"electron-packager": "^5.0.2",
"electron-prebuilt": "^0.31.0",
"gulp": "^3.9.0",
"gulp-babel": "^5.2.1",
"gulp-load-plugins": "^0.10.0"
}
}
gulp-babel を使っているけれど、ES6 で書いているのではなくて jsx をコンパイルしたかっただけです。また、普段は Titanium でアプリを書いているので、Backbone.js に慣れているのですが、今回はお勉強ということで React を使ってみました。コンパイルされると、app フォルダの中に chat.js として書き出されます。
アプリのエントリーポイント
エントリーポイントになる main.js はチャットルームの取得をして、トレイにメニューを作ります。Slack みたいにウィンドウを作ってチャットルームを並べても良いのですが、トレイからサッとアクセスして閲覧&書き込みが終わったらすぐにウィンドウを閉じたいので、トレイ常駐型な感じです。
nw.js ではウィンドウ間の引数渡しが非常に困難でしたが、Electron はとっても簡単です。今回は ChatWork の API トークンとチャットルームの ID 等を引き渡しています。なんと、カスタムプロパティに設定してあげると、子ウィンドウで参照することができるんですね。ただし、文字列のみっぽいです。JSON であれば JSON.stringify
してから渡してあげれば OK ですね。
main.js でウィンドウを開いて、
var window = new BrowserWindow();
window.token = token;
window.loadUrl('file://' + path.join(__dirname, 'app', 'html', 'chat.html'));
chat.jsx で引数を受け取ります。
var remote = require('remote'),
token = remote.getCurrentWindow().token,
roomId = remote.getCurrentWindow().roomId;
nw.js の場合はウィンドウを開くときの URL に引数として繋げていました。パースするのも面倒ですし、セキュリティ的にダメダメですよね...。
タイムラインを表示する
src/chat.jsx でタイムラインウィンドウを表示します。コンパイルされると app/chat.js ですね。
タイムラインウィンドウを表示するために Virtual DOM を使います。根本となる Timeline クラス、一つ一つのメッセージを表示するための Message クラス、最後にメッセージ入力のための Textarea クラスで構成されています。
メッセージを送信する「Shift+Enter」のキーイベントの取得が面倒でした。onKeyDown
でシフトの監視&フラグ立てをして、onKeyPress
でシフトフラグが立っている&エンターが押された際にポストするようにしています。もっとかっちょいい方法があれば教えて下さい。そして、メッセージのポストが正常に完了したら、テキストエリアの中身を消してあげます。
getInitialState: function(){
return {
message: '',
shift: false
};
},
doKeyDown: function(e){
if (e.which === 16) {
this.state.shift = true;
}
},
doKeyPress: function(e){
var that = this;
if (e.which === 13 && this.state.shift && that.state.message !== '') {
request.post({
url: 'https://api.chatwork.com/v1/rooms/' + roomId + '/messages',
headers: {
'X-ChatWorkToken': token
},
form: {
body: this.state.message
}
}, function(error, response, body){
if (error) {
return;
}
var node = React.findDOMNode(that);
node.value = '';
timeline.componentDidMount();
});
}
}
メッセージの出力ですが、チャットワークの独自タグを上手く表示させるのに心が折れたので、To と Re だけそれなりに表示しています。久々に HTML / CSS を書いて、かなり忘れていてダメダメでした。
結構テキトーに書いてみたのですが、それなりにできてしまったので Electron 楽しいです。nw.js も似たようなものなので比べてみると良いかもですね。nw.js からのポーティングは、ほぼコピペで終わりました。タイムラインウィンドウを常に最前面に表示、とか機能追加していきたいですね。