LoginSignup
47
48

More than 5 years have passed since last update.

Electron で Live Dwango Reader(旧 Live Door Reader)クライアントを作ってみる

Last updated at Posted at 2015-09-14

まだまだ機能は乏しいし、既読化も実装していません(テストでフィードがなくなっちゃうと痛いので後回し)が、ぼく自身、常用しているショートカットまでを実装できたので、ご紹介。

レポジトリはこちら。

capture1.png

capture2.png

使っている npm パッケージ

  • React:コンポーネントとかレンダリングとか
  • react-modal:元記事表示用のモーダルを作る時に
  • mousetrap:キーボードショートカットで
  • request:API と通信
  • lodash:なんやかんやでいつも使う

できること

  • フィード一覧の取得、表示
  • 選択されたフィードを読み込んで、記事一覧を表示
  • 元記事の表示
  • 最低限のキーボードショートカット

(まだ)できないこと

  • 認証
  • その他いっぱい

構成

main.js
app/
  component/
    feeds.js
    items.js
  html/
    index.html
  style/
    style.css
  app.js
src/
  feeds.jsx
  items.jsx

main.js がアプリのエントリーポイントになります。package.json からアクセストークンを取得してウィンドウを作っているだけですね。app/html/index.html がウィンドウに読み込まれます。

app/html/index.html は VirtualDOM のコンテナを作って、app/app.js を読み込んでいるだけです。コンテナは、サイドバーのフィード一覧と、フィードが読み込まれた際のアイテム一覧、元記事表示ようのモーダルになります。あとは CSS を読み込んだり。

app/app.js で実際にページを作っていきます。API を叩いているだけですね。mousetrap で r キーをフィード一覧の更新に割り当てていますが、サイドバーをまるっと読み込みなおしています。たぶん React 的にもっとかっちょいい方法があると思いますが、今後書きなおすかもしれません。

app/component/feeds.js は、src/feeds.jsx からコンパイルされて作られます。サイドバーのフィード一覧ですね。mousetrap で as が割り当てられています。前と次のフィードへ戻ったり進んだりするショートカットです。

render の中で <ul> と <li> を作っているのですが、当初、<li> は別コンポーネントだったんですね。マウスオーバー・アウトした時やショートカットで移動する時、コンポーネント間のやりとりはどうするんだろう?って感じだったのですが、ドキュメントを見てみると、この書き方が一般的なようで書き直しました。

render: function(){
    return (
        <ul>{this.props.feeds.map(function(item, index){
                return (
                    <Feed item={item}/>
                );
            }, this)}
        </ul>
    );
}

これが、

render: function(){
    return (
        <ul>{this.props.feeds.map(function(item, index){
                return (
                    <li key={item.subscribe_id}
                        onMouseOver={this.doMouseOver.bind(this, index)}
                        onMouseOut={this.doMouseOut.bind(this, index)}
                        onClick={this.doClick.bind(this, index)}>
                        <img src={item.icon}/> {item.title} ({item.unread_count})
                    </li>
                );
            }, this)}
        </ul>
    );
}

こうなりました。

書いてみると、なるほどね、と納得です。クリックやマウスオーバー・アウトのイベントが一つ一つのコンポーネントに貼られてしまうし、this.props.feeds があるので他のフィードを意識できるしで、コンポーネントを分けすぎるのもダメですねって感じでした。
あとは、フィードをクリックすれば記事のアイテムが右ペインに読み込まれます。ほぼ LDR と同じですね。

app/component/items.js は、src/items.jsx からコンパイルされて作られます。記事のアイテムがずらーっと読み込まれます。mousetrap で j k v n が割り当てられています。記事の移動と元記事の開閉になります。なんでぼくが LDR のアプリ化をしたかというと、ほぼ、ココのためになります。ブラウザだと、元記事開くとタブが開かれるんですよね。タブを移動したくないのです。このアプリでは v で react-modal を使って、モーダルで元記事を開きます。LDR にはありませんが、n で元記事のモーダルを閉じます。
app/component/feeds.js と同じように子コンポーネントを作っていたのですが、ここも書き直しました。記事の移動のためですね。フィードの移動はクリックイベントを偽装していたのですが、記事の移動は scrollIntoView を使っています。

元記事の表示には Electron の <webview> を使っています。ここ、React って楽だなあと思ったところです。srcthis.state.url になっていますので、this.setState({ url: '元記事の URL' }); してあげるだけで表示する元記事を切り替えられます。とっても楽!モーダルの開閉も this.state 使っていますね。ホント楽!

render: function(){
    return (
        <ul>{this.props.items.map(function(item, index){
                return (
                    <li id={item.id} key={item.id}>
                        <p style={style.title} onClick={this.doOpen.bind(this, index)}>{item.title}</p>
                        <div dangerouslySetInnerHTML={{__html: item.body}}/>
                    </li>
                );
            }, this)}
            <Modal isOpen={this.state.modalIsOpen}>
                <div style={style.close}><button onClick={this.doClose}>閉じる</button></div>
                <webview src={this.state.url} style={style.browser}></webview>
            </Modal>
        </ul>
    );
}

うごかしかた

まずはソースコードをチェックアウトしてきましょう。

$ git clone git@github.com:k0sukey/Electron-LDR.git
$ cd Electron-LDR

で、npm パッケージをインストールします。

$ npm install

認証ができていないので、http://api.ma.la/reader.html からアクセストークンをもらってきます。authorize して、インスペクタ等でローカルストレージにあるアクセストークンを package.json にコピペしてください。有効期限が切れたらもう一度 authorize すれば OK です。

{
  "access_token": "****************************************",
  "token_type": "Bearer",
  "expires_in": 7200,
  "created_at": 1442133518
}

↑の access_token を、

{
  "name": "Electron-LDR",
  // 
  "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"
  },
  "token": "ここにコピペしてください"
}

コマンドラインで起動します。

$ npm start

まとめ

あらためて、Electron と React って親和性高いなって思いました。あと、自前で認証できないのは致命的ですね...。何とかしないと。

47
48
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
48