Electron と React でPomodoro Timerアプリを作ってみる

  • 51
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

React.js, JSX, ElectronでPomodoro Technique(ポモドーロテクニック)を実践できるタイマーアプリを作ってみました。

https://github.com/massa142/pomodoro

pomodoro_timer.png

作った理由

PHPカンファレンス2015でのKenji Akiyamaさんの発表資料を見て、「Electron面白そう!ちょっと試してみよ」となったためです。
Electronからクロスプラットフォーム・アプリケーションの歴史を考える

ただ作りたいものがパッと思いつかなかったので、Akiyamaさんと同じPomodoro Technique用のタイマーアプリをReact.jsで作ってみることにしました。

Pomodoro Techniqueとは

1.達成しようとするタスクを選ぶ
2.キッチンタイマーで25分を設定する
3.タイマーが鳴るまでタスクに集中する
4.少し休憩する(5分程度でOK)
5.ステップ2~4を4回繰り返したら、少し長めに休憩する

今日から始める生産性アップ術。ポモドーロ・テクニック再入門ガイド

rebuild.fm ep.93でもちょっと話題にあがってました。
http://rebuild.fm/93/

環境構築

ElectronもReactも今回初めて触ったので開発環境整えるのどうしようかなーと思ってたら、@Quramyさんが「ぼくのかんがえたさいきょうのElectron」開発環境を作ってくれていました。
ありがたくこのboilerplateを使わせて頂きました。

内部構成

.
├── README.md
├── bower.json
├── bower_components
├── dist
├── gulpfile.js
├── node_modules
├── package.json
├── release
└── src
    ├── app.js
    ├── assets
    │   └── images
    │       ├── electron.svg
    │       └── electron2.svg
    ├── browser
    │   └── menu
    │       └── appMenu.js
    ├── renderer
    │   ├── bootstrap.js
    │   ├── components
    │   │   └── main.jsx
    │   └── index.html
    └── styles
        └── main.scss

main.jsx

'use strict';

import React from 'react';
import notifier from 'node-notifier';

export class Main extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      isStart: false,
      isBreak: false,
      time: this.props.duration
    }
    this.handleStartClicked = ::this.handleStartClicked;
    this.handleResetClicked = ::this.handleResetClicked;
    this.tick = ::this.tick;
  }

  handleStartClicked() {
    if (!this.state.isStart) {
      this.interval = setInterval(this.tick, 1000);
      this.setState({isStart: true});
    } else {
      clearInterval(this.interval);
      this.setState({isStart: false});
    }
  }

  handleResetClicked() {
    this.reset();
  }

  tick() {
    this.setState({time: this.state.time - 1});
    if (this.state.time === 0) {
      this.finishEvent();
    }
  }

  finishEvent() {
    if (this.state.isBreak) {
      this.reset();
      this.notify('Break is over!');
    } else {
      this.break();
      this.notify('Good work!');
    }
  }

  break() {
    this.setState({
      isStart: true,
      isBreak: true,
      time: this.props.breakTime
    });
  }

  reset() {
    clearInterval(this.interval);
    this.setState({
      isStart: false,
      isBreak: false,
      time: this.props.duration
    });
  }

  notify(message) {
    notifier.notify({
      'title': 'Pomodoro Timer',
      'message': message
    });
  }

  converter = {
    s2m(s) {
      let minutes = Math.floor(s / 60);
      let seconds = s % 60;
      return ('0' + minutes.toString()).slice(-2) + ':' + ('0' + seconds.toString()).slice(-2);
    }
  };

  render() {
    return (
      <div className="container">
        <div className="jumbotron main">
          <h2>Pomodoro Timer</h2>
          <img src={this.state.isBreak ? "../assets/images/electron2.svg" : "../assets/images/electron.svg"} alt="" width="128px"></img>
          <h2>{this.converter.s2m(this.state.time)}</h2>
          <div>
            <button type="button" className="btn btn-primary" onClick={this.handleStartClicked}>
              {this.state.isStart ? "Pause" : "Start"}
            </button>
            <button type="button" className="btn btn-warning" onClick={this.handleResetClicked}>Reset</button>
          </div>
        </div>
      </div>
    );
  }
}

Main.defaultProps = {
  duration: 1500,
  breakTime: 300
};

感想